Recent from talks
Contribute something
Nothing was collected or created yet.
Ziggurat algorithm
View on WikipediaThe ziggurat algorithm is an algorithm for pseudo-random number sampling. Belonging to the class of rejection sampling algorithms, it relies on an underlying source of uniformly-distributed random numbers, typically from a pseudo-random number generator, as well as precomputed tables. The algorithm is used to generate values from a monotonically decreasing probability distribution. It can also be applied to symmetric unimodal distributions, such as the normal distribution, by choosing a value from one half of the distribution and then randomly choosing which half the value is considered to have been drawn from. It was developed by George Marsaglia and others in the 1960s.
A typical value produced by the algorithm only requires the generation of one random floating-point value and one random table index, followed by one table lookup, one multiply operation and one comparison. Sometimes (2.5% of the time, in the case of a normal or exponential distribution when using typical table sizes)[citation needed] more computations are required. Nevertheless, the algorithm is computationally much faster[citation needed] than the two most commonly used methods of generating normally distributed random numbers, the Marsaglia polar method and the Box–Muller transform, which require at least one logarithm and one square root calculation for each pair of generated values. However, since the ziggurat algorithm is more complex to implement it is best used when large quantities of random numbers are required.
The term ziggurat algorithm dates from Marsaglia's paper with Wai Wan Tsang in 2000; it is so named because it is conceptually based on covering the probability distribution with rectangular segments stacked in decreasing order of size, resulting in a figure that resembles a ziggurat.

Theory of operation
[edit]The ziggurat algorithm is a rejection sampling algorithm; it randomly generates a point in a distribution slightly larger than the desired distribution, then tests whether the generated point is inside the desired distribution. If not, it tries again. Given a random point underneath a probability density curve, its x coordinate is a random number with the desired distribution.
The distribution the ziggurat algorithm chooses from is made up of n equal-area regions; rectangles that cover the bulk of the desired distribution, on top of a non-rectangular base that includes the tail of the distribution.
Given a monotone decreasing probability density function , defined for all , the base of the ziggurat is defined as all points inside the distribution and below . This consists of a rectangular region from to , and the (typically infinite) tail of the distribution, where .
This layer (call it layer 0) has area A. On top of this, add a rectangular layer of width and height , so it also has area . The top of this layer is at height and intersects the density function at a point , where . This layer includes every point in the density function between and , but (unlike the base layer) also includes points such as which are not in the desired distribution.
Further layers are then stacked on top. To use a precomputed table of size (n = 256 is typical), one chooses such that , meaning that the top box, layer , reaches the distribution's peak at exactly.
Layer extends vertically in range , and can be divided into two regions horizontally: the (generally larger) portion in range which is entirely contained within the desired distribution, and the (small) portion in range , which is only partially contained.
Ignoring for a moment the problem of layer 0, and given uniform random variables and , the ziggurat algorithm can be described as:
- Choose a random layer .
- Let .
- If , return .
- Let .
- Compute . If , return .
- Otherwise, choose new random numbers and go back to step 1.
Step 1 amounts to choosing a low-resolution y coordinate. Step 3 tests if the x coordinate is clearly within the desired density function without knowing more about the y coordinate. If it is not, step 4 chooses a high-resolution y coordinate, and step 5 does the rejection test.
With closely spaced layers, the algorithm terminates at step 3 a very large fraction of the time. For the top layer , however, this test always fails, because .
Layer 0 can also be divided into a central region and an edge, but the edge is an infinite tail. To use the same algorithm to check if the point is in the central region, generate a fictitious . This will generate points with with the correct frequency, and in the rare case that layer 0 is selected and , use a special fallback algorithm to select a point at random from the tail. Because the fallback algorithm is used less than one time in a thousand, speed is not essential.
Thus, the full ziggurat algorithm for one-sided distributions is:
- Choose a random layer .
- Let
- If , return .
- If , generate a point from the tail using the fallback algorithm.
- Let .
- Compute . If , return .
- Otherwise, choose new random numbers and go back to step 1.
For a two-sided distribution, the result must be negated 50% of the time. This can often be done conveniently by choosing and, in step 3, testing if .
Fallback algorithms for the tail
[edit]Because the ziggurat algorithm only generates most outputs very rapidly, and requires a fallback algorithm whenever it is always more complex than a more direct implementation. The specific fallback algorithm depends on the distribution.
For an exponential distribution, the tail looks just like the body of the distribution. One way is to fall back to the most elementary algorithm E = −ln(U1) and let x = x1 − ln(U1). Another is to call the ziggurat algorithm recursively and add x1 to the result.
For a normal distribution, Marsaglia suggests a compact algorithm:
- Let x = −ln(U1)/x1.
- Let y = −ln(U2).
- If 2y > x2, return x + x1.
- Otherwise, go back to step 1.
Since x1 ≈ 3.5 for typical table sizes, the test in step 3 is almost always successful. Since −ln(U1) is an exponentially distributed variate, an implementation of the exponential distribution may be used.
Optimizations
[edit]The algorithm can be performed efficiently with precomputed tables of xi and yi = f(xi), but there are some modifications to make it even faster:
- Nothing in the ziggurat algorithm depends on the probability distribution function being normalized (integral under the curve equal to 1), removing normalizing constants can speed up the computation of f(x).
- Most uniform random number generators are based on integer random number generators which return an integer in the range [0, 232 − 1]. A table of 2−32xi lets you use such numbers directly for U0.
- When computing two-sided distributions using a two-sided U0 as described earlier, the random integer can be interpreted as a signed number in the range [−231, 231 − 1], and a scale factor of 2−31 can be used.
- Rather than comparing U0xi to xi +1 in step 3, it is possible to precompute xi +1/xi and compare U0 with that directly. If U0 is an integer random number generator, these limits may be premultiplied by 232 (or 231, as appropriate) so an integer comparison can be used.
- With the above two changes, the table of unmodified xi values is no longer needed and may be deleted.
- When generating IEEE 754 single-precision floating point values, which only have a 24-bit mantissa (including the implicit leading 1), the least-significant bits of a 32-bit integer random number are not used. These bits may be used to select the layer number. (See the references below for a detailed discussion of this.)
- The first three steps may be put into an inline function, which can call an out-of-line implementation of the less frequently needed steps.
Generating the tables
[edit]It is possible to store the entire table precomputed, or just include the values n, y1, A, and an implementation of f −1(y) in the source code, and compute the remaining values when initializing the random number generator.
As previously described, you can find xi = f −1(yi) and yi +1 = yi + A/xi. Repeat n − 1 times for the layers of the ziggurat. At the end, you should have yn = f(0). There will be some round-off error, but it is a useful sanity test to see that it is acceptably small.
When actually filling in the table values, just assume that xn = 0 and yn = f(0), and accept the slight difference in layer n − 1's area as rounding error.
Finding x1 and A
[edit]Given an initial (guess at) x1, you need a way to compute the area t of the tail for which x > x1. For the exponential distribution, this is just e−x1, while for the normal distribution, assuming you are using the unnormalized f(x) = e−x2/2, this is √π/2 erfc(x/√2). For more awkward distributions, numerical integration may be required.
With this in hand, from x1, you can find y1 = f(x1), the area t in the tail, and the area of the base layer A = x1y1 + t.
Then compute the series yi and xi as above. If yi > f(0) for any i < n, then the initial estimate x1 was too low, leading to too large an area A. If yn < f(0), then the initial estimate x1 was too high.
Given this, use a root-finding algorithm (such as the bisection method) to find the value x1 which produces yn−1 as close to f(0) as possible. Alternatively, look for the value which makes the area of the topmost layer, xn−1(f(0) − yn−1), as close to the desired value A as possible. This saves one evaluation of f −1(x) and is actually the condition of greatest interest.
McFarland's variation
[edit]Christopher D. McFarland has proposed a further-optimized version.[1] This applies three algorithmic changes, at the expense of slightly larger tables.
First, the common case considers only the rectangular portions, from (0, yi −1) to (xi, yi) The odd-shaped regions to the right of these (mostly almost triangular, plus the tail) are handled separately. This simplifies and speeds up the algorithm's fast path.
Second, the exact area of the odd-shaped regions is used; they are not rounded up to include the entire rectangle to (xi −1, yi). This increases the probability that the fast path will be used.
One major consequence of this is that the number of layers is slightly less than n. Even though the area of the odd-shaped portions is taken exactly, the total adds up to more than one layer's worth. The area per layer is adjusted so that the number of rectangular layers is an integer. If the initial 0 ≤ i < n exceeds the number of rectangular layers, phase 2 proceeds.
If the value sought lies in any of the odd-shaped regions, the alias method is used to choose one, based on its true area. This is a small amount of additional work, and requires additional alias tables, but chooses one of the layers' right-hand sides.
The chosen odd-shaped region is rejection sampled, but if a sample is rejected, the algorithm does not return to the beginning. The true area of each odd-shaped region was used to choose a layer, so the rejection-sampling loop stays in that layer until a point is chosen.
Third, the almost-triangular shape of most odd-shaped portions is exploited, although this must be divided into three cases depending on the second derivative of the probability distribution function in the selected layer.
If the function is convex (as the exponential distribution is everywhere, and the normal distribution is for |x| > 1), then the function is strictly contained within the lower triangle. Two unit uniform deviates U1 and U2 are chosen, and before they are scaled to the rectangle enclosing the odd-shaped region, their sum is tested. If U1 + U2 > 1, the point is in the upper triangle and can be reflected to (1−U1, 1−U2). Then, if U1 + U2 < 1−ε, for some suitable tolerance ε, the point is definitely below the curve and can immediately be accepted. Only for points very close to the diagonal is it necessary to compute the distribution function f(x) to perform an exact rejection test. (The tolerance ε should in theory depend on the layer, but a single maximum value can be used on all layers with little loss.)
If the function is concave (as the normal distribution is for |x| < 1), it includes a small portion of the upper triangle so reflection is impossible, but points whose normalized coordinates satisfy U1 + U2 ≤ 1 can be immediately accepted, and points for which U1 + U2 > 1+ε can be immediately rejected.
In the one layer which straddles |x| = 1, the normal distribution has an inflection point, and the exact rejection test must be applied if 1−ε <U1 + U2 < 1+ε.
The tail is handled as in the original Ziggurat algorithm, and can be thought of as a fourth case for the shape of the odd-shaped region to the right.
References
[edit]- ^ McFarland, Christopher D. (24 June 2015). "A modified ziggurat algorithm for generating exponentially and normally distributed pseudorandom numbers". Journal of Statistical Computation and Simulation. 86 (7): 1281–1294. arXiv:1403.6870. doi:10.1080/00949655.2015.1060234. PMC 4812161. PMID 27041780. Note that the Bitbucket repository mentioned in the paper is no longer available and the code is now at https://github.com/cd-mcfarland/fast_prng
- Marsaglia, George; Tsang, Wai Wan (2 October 2000). "The Ziggurat Method for Generating Random Variables". Journal of Statistical Software. 5 (8). doi:10.18637/jss.v005.i08. Retrieved 2007-06-20. This paper numbers the layers from 1 starting at the top, and makes layer 0 at the bottom a special case, while the explanation above numbers layers from 0 at the bottom.
- C implementation of the ziggurat method for the normal density function and the exponential density function, that is essentially a copy of the code in the paper. (Potential users should be aware that this C code assumes 32-bit integers.)
- A C# implementation of the ziggurat algorithm and overview of the method.
- Jurgen A. Doornik (2005). "An Improved Ziggurat Method to Generate Normal Random Samples" (PDF). Nuffield College, Oxford. Retrieved 2007-06-20. Describes the hazards of using the least-significant bits of the integer random number generator to choose the layer number.
- Normal Behavior By Cleve Moler, MathWorks, describing the ziggurat algorithm introduced in MATLAB version 5, 2001.
- The Ziggurat Random Normal Generator Blogs of MathWorks, posted by Cleve Moler, May 18, 2015.
- David B. Thomas; Philip H.W. Leong; Wayne Luk; John D. Villasenor (October 2007). "Gaussian Random Number Generators" (PDF). ACM Computing Surveys. 39 (4): 11:1–38. doi:10.1145/1287620.1287622. ISSN 0360-0300. S2CID 10948255. Retrieved 2009-07-27.
[W]hen maintaining extremely high statistical quality is the first priority, and subject to that constraint, speed is also desired, the Ziggurat method will often be the most appropriate choice.
Comparison of several algorithms for generating Gaussian random numbers. - Nadler, Boaz (2006). "Design Flaws in the Implementation of the Ziggurat and Monty Python methods (And some remarks on Matlab randn)". arXiv:math/0603058.. Illustrates problems with underlying uniform pseudo-random number generators and how those problems affect the ziggurat algorithm's output.
- Edrees, Hassan M.; Cheung, Brian; Sandora, McCullen; Nummey, David; Stefan, Deian (13–16 July 2009). Hardware-Optimized Ziggurat Algorithm for High-Speed Gaussian Random Number Generators (PDF). 2009 International Conference on Engineering of Reconfigurable Systems & Algorithms. Las Vegas. Archived from the original (PDF) on 24 January 2013. Retrieved 29 September 2016.
- Marsaglia, George (September 1963). Generating a Variable from the Tail of the Normal Distribution (Technical report). Boeing Scientific Research Labs. Mathematical Note No. 322, DTIC accession number AD0423993. Archived from the original on September 10, 2014 – via Defense Technical Information Center.
Ziggurat algorithm
View on GrokipediaIntroduction
Definition and Purpose
The Ziggurat algorithm is a rejection sampling technique designed to generate pseudorandom variates from target probability density functions (PDFs), particularly those that are decreasing or unimodal, such as the Gaussian, exponential, or gamma distributions.[1] In rejection sampling, uniform random numbers are used to sample points from a proposal distribution that envelopes the target density , accepting only those points that fall below the target curve to ensure the correct distribution is approximated.[1] The core purpose of the Ziggurat method is to enable fast generation of such variates, especially where direct methods like inverse transform sampling are computationally inefficient due to the need for solving complex equations or handling unbounded supports.[1] Developed by George Marsaglia and Wai Wan Tsang, it prioritizes speed for large-scale simulations requiring millions of samples.[1] Conceptually, the algorithm constructs a "ziggurat" by stacking horizontal rectangles beneath the target density curve, forming a stepped approximation that covers the PDF from its mode downward, with each layer representing an equal-area region for efficient uniform sampling.[1] This structure, named after the ancient Mesopotamian stepped pyramids, allows for rapid proposal generation by selecting a rectangle and sampling a point uniformly within it, minimizing the envelope's excess area over the target density to reduce rejections.[1] The rectangles are arranged such that the topmost layer aligns with the PDF's peak, and subsequent layers decrease in height while extending further along the x-axis, providing a tight bound particularly suited to heavy-tailed or unbounded distributions like the normal.[1] For a sampled point where is uniform on and falls within a selected rectangle, the acceptance criterion is to retain if , ensuring the generated variates follow the target distribution .[1] This setup yields high acceptance rates, often around 99%, which significantly lowers computational overhead compared to traditional inverse methods.[1] The algorithm was specifically engineered for high-speed performance in numerical simulations, achieving rates such as 15 million normal variates per second on a 400 MHz processor using optimized C implementations.[1]Historical Background
The Ziggurat algorithm traces its origins to rejection sampling techniques pioneered in the mid-20th century, with foundational work in the 1960s including the rectangle-wedge-tail method developed by George Marsaglia, Marshall D. MacLaren, and Thomas A. Bray for efficient generation of normal random variates.[3] This approach laid the groundwork by using geometric coverings to accelerate sampling from unimodal densities, though it was computationally intensive for tails. The Ziggurat method emerged as a table-based refinement of such ideas, formalizing a layered rectangle structure to minimize rejection rates. In the early 1980s, Marsaglia and Wai Wan Tsang began developing the core concepts for sampling from decreasing densities, culminating in their 1984 publication, "A Fast, Easily Implemented Method for Sampling from Decreasing or Symmetric Unimodal Density Functions," which introduced an efficient acceptance-rejection framework using stacked rectangles.[4] This work, initially focused on practical implementation for symmetric and decreasing distributions, represented an internal evolution toward streamlined random variate generation but remained less widely disseminated until later refinements. The algorithm received its name and broader recognition in the seminal 2000 paper by Marsaglia and Tsang, "The Ziggurat Method for Generating Random Variables," which presented the one-dimensional version tailored for the normal distribution and emphasized its speed through precomputed tables.[1] This publication marked the evolution of the 1984 method into a publicly accessible, optimized technique, capable of generating millions of variates per second on contemporary hardware. Extensions in the same paper adapted the approach for the exponential distribution and other decreasing densities, enhancing its versatility.[1] Further evolution came in 2005 with a comment on the implementation by Philip H.W. Leong and colleagues, who proposed improvements addressing potential issues with the uniform random number generator in prior versions.[5] The algorithm's practical impact was solidified in the late 1990s through its adoption in MATLAB's randn function for normal random number generation, influencing widespread use in statistical software and simulations.[6]Theoretical Foundation
Rectangle Covering Approach
The rectangle covering approach forms the core of the Ziggurat algorithm, providing a geometric approximation to the target probability density function that facilitates efficient rejection sampling. The density is assumed to be positive and monotonically decreasing for , and integrable over the real line with total integral equal to 1. A canonical example is the standard normal density, given by . This setup exploits the decreasing nature of on the positive axis (with symmetry for even functions like the normal), allowing a piecewise constant under-approximation via rectangles.[1] The Ziggurat structure employs rectangles, indexed from to , stacked in a staircase fashion symmetric about zero. The -th rectangle has base width and height , where and . The outermost rectangle () handles the infinite tail, while inner rectangles narrow toward the origin, creating a stepped boundary that remains entirely beneath the curve of . This configuration ensures the union of the rectangles covers a region of probability mass at most 1, serving as a proposal distribution for sampling.[1] The covering property guarantees that every point within the rectangles satisfies , enabling uniform random points generated inside a rectangle to be accepted as samples from with probability proportional to via rejection, or directly accepted if below the curve. The area of the -th rectangle is (considering the positive side, with symmetry doubling for the full base), and the total area is . To achieve balanced coverage, the breakpoints for the decreasing are selected such that the tail integral , promoting approximately equal areas across rectangles initially.[1] This staircase approximation is distinctive in its efficiency, as the majority of proposed samples—up to 99% in typical implementations for distributions like the normal—fall within regions where no density evaluation is needed, relying solely on uniform generation and simple comparisons for acceptance.[1]Table Generation Process
The table generation process in the Ziggurat algorithm occurs during an offline precomputation phase, where the lookup tables are constructed once at initialization based on the target probability density function . This phase is essential for enabling efficient runtime sampling and typically employs or rectangular layers to strike a balance between computational speed during generation and approximation accuracy of the density. The tables store the boundary points and associated constants, such as widths or scaling factors, allowing for rapid uniform selection across the layers.[1][2] The construction proceeds iteratively, often starting from the outer layer near the tail of the distribution and working inward toward the origin to ensure proper coverage. For the standard normal distribution, the process begins with an initial , selected to optimize the overall acceptance probability. Subsequent boundaries are computed by solving for such that each rectangle approximates equal area , using the relation derived from the density: , which inverts the normal density to position the next boundary while maintaining coverage. An approximation for the normal case refines this as , where captures the local curvature of . This iterative approach continues until the innermost layer, ensuring the rectangles align with the decreasing nature of .[1][2] Heights for each rectangle are assigned as to guarantee that the constant-height rectangle fully covers the density curve within its interval ; however, for regions with convex tails (where the second derivative of is positive), is used to tighten the bound while preserving coverage, leveraging the log-concavity of distributions like the normal. The area of the -th rectangle is then , with all rectangles designed to have uniform areas . To achieve this uniformity, the boundaries satisfy the recursive equation , solved iteratively from the tail inward, accounting for the remaining probability mass.[1][2] Finally, normalization ensures that the total probability covered by the tables satisfies (tail area) , with the tail area computed as the integral beyond . The resulting tables are stored as arrays of the values and related constants (e.g., scaling factors for 32-bit integer indexing); for the normal distribution, the optimal setup yields on the positive side, enabling high efficiency with acceptance rates exceeding 98%. These precomputed values are hardcoded or generated at program startup for portability across implementations.[1][2]Parameter Determination
The parameter represents the width of the base rectangle in the Ziggurat covering, serving as the cutoff beyond which the bottom layer extends fully under the density function . It is selected to strike a balance between the number of rectangular layers and the overall rejection rate, often at the point where is maximized, ensuring the envelope efficiently approximates the density near the origin while keeping table sizes manageable. The primary optimization criterion involves maximizing the acceptance probability , where is the total area of the rectangular envelope, equivalent to minimizing for a fixed . This is achieved by maximizing under the constraint that the rectangles cover the density effectively, as larger reduces the relative contribution of the tail but may increase rejections in lower layers if not balanced with appropriate , the common area per rectangle.[2] For the standard normal distribution with density , the value of is determined by solving the equation where is minimized, leading to the iterative approximation . This condition arises from optimizing the base rectangle's contribution to the envelope, ensuring the slope aligns with the density's decay for minimal overhead in subsequent layers. Numerical solution via iteration or root-finding yields .[2] The parameter is then computed as average rectangle area. For the standard normal using the unnormalized density , this results in , precomputed during setup to eliminate runtime overhead. To find numerically for general densities, binary search or Newton-Raphson methods are applied to the tail integral equation, targeting the derivative condition at the base, ensuring the tail handling integrates seamlessly with the rectangular stack. These methods converge quickly due to the monotonic decay of , typically requiring fewer than 10 iterations for precision to machine epsilon. For typical implementations with 128 slabs, the overall acceptance rate is approximately 98.8%.[2]Algorithm Mechanics
Sampling and Acceptance-Rejection
The sampling procedure in the Ziggurat algorithm utilizes rejection sampling to generate random variates from a target decreasing density function using the precomputed tables of layer boundaries and heights . Three independent uniform random numbers serve as inputs: . The layer index is selected uniformly as , where is the number of main slabs (typically 128 or 256).[1] A candidate point is then generated uniformly within the selected layer as where the slabs are defined with , and is the height of slab (touching the density at the left boundary ). The widths satisfy for common area .[1] To perform the acceptance test, generate the vertical coordinate . Accept if , or equivalently ; otherwise, reject and restart with new uniforms. This setup ensures the stepped envelope for satisfies since is decreasing, yielding high acceptance rates (exceeding 98% overall for the normal distribution) due to the tight fit of the rectangles beneath the curve.[1] Optimized variants employ 32-bit integer uniforms throughout to replace floating-point operations with bit shifts and multiplications, achieving generation rates of up to 15 million variates per second on 400 MHz processors.[1]Tail Handling Techniques
In the Ziggurat algorithm, the tail region corresponds to the portion of the target density beyond the lowest rectangle at , with probability mass ; this is typically kept small (e.g., ~0.001) through the selection of an appropriate number of layers . Tail slab selection occurs with probability approximately , and is chosen to simplify sampling from the tail using dedicated subroutines.[1] When the selected layer is the tail (i.e., equivalent to ), the algorithm switches to a slower, exact fallback method to generate a variate from the conditional tail distribution , such as the inverse cumulative distribution function or a specialized rejection sampler. If the tail subroutine rejects the candidate, the entire Ziggurat sampling process is restarted to ensure unbiased generation.[1] For the normal distribution, tail handling often employs Marsaglia's rejection method involving two logarithmic uniforms: generate , until , then return , where is scaled appropriately from .[1] (Marsaglia, G. (2000). The Ziggurat method for generating normal variables. Unpublished note.) For the exponential distribution, the tail benefits from the memoryless property, allowing direct inversion: , where is uniform on (0,1) and is the rate parameter, yielding an exact sample from the conditional tail without rejection.[1] In cases like the gamma distribution, the tail is optimized for simplicity by integrating methods such as Johnk's rejection sampler, which efficiently handles the remaining probability mass.[1] (Johnk, M. D. (1964). Note: A generator for the gamma distribution with mean 1/2 and coefficient of variation 2. Communications of the ACM, 7(10), 604.)Performance Optimizations
The Ziggurat algorithm's performance can be significantly enhanced through careful tuning of the table size, typically set to N=128 layers for the standard normal distribution, which strikes an optimal balance between memory usage—approximately 512 bytes for the integer-based tables—and computational speed. This configuration minimizes setup overhead while maintaining high efficiency in sampling, as larger values of N, such as 256, further reduce the frequency of tail region accesses but increase the initial table computation time due to more extensive numerical integrations.[2] Implementations often employ 32-bit integers for generating uniform random numbers, enabling fast bit-shifting operations to approximate layer indexing without floating-point conversions; for instance, the layer index i can be computed as i = floor(2^{32} \times U / A), where U is a uniform random variate and A is a scaling factor derived from the table widths. This integer arithmetic avoids costly divisions and multiplications in the inner loop, contributing to the algorithm's speed advantage over traditional methods. Precomputing reciprocal heights 1/h_i allows rejection checks to use efficient multiplications instead of divisions, further accelerating the process.[2] To minimize rejection rates and improve overall throughput, the boundary x_1 between the main ziggurat body and the tail is adjusted during table setup to ensure an acceptance probability exceeding 0.98; for the normal distribution with N=128, this yields an approximate rate of 0.987, meaning over 98% of samples are accepted on the first try without fallback computations. A key optimization for the topmost layer involves an immediate acceptance shortcut: if the layer index corresponds to the uppermost rectangle (often i=0 in certain implementations) and the uniform coordinate u < 1, the sample is returned directly, as this region lies entirely beneath the target density function, eliminating any rejection test.[2] For modern hardware, table storage is aligned to exploit cache hierarchies and SIMD instructions, ensuring rapid access during vectorized operations and keeping the compact tables (fitting within L1 cache) for low-latency retrieval. On GPUs, the algorithm's rejection-sampling structure lends itself to massive parallelism, allowing independent thread blocks to generate batches of variates simultaneously for large-scale simulations, with reported throughputs surpassing CPU implementations by orders of magnitude in parallel environments.[2][7]Variations and Extensions
McFarland's Modification
In 2014, Christopher D. McFarland proposed a refinement to the Ziggurat algorithm specifically tailored for generating pseudorandom numbers from normal and exponential distributions, published as "A modified ziggurat algorithm for generating exponentially- and normally-distributed pseudorandom numbers."[8] This modification repositions the rectangular layers beneath the target density function rather than above it, which eliminates the need for rejection tests in the vast majority of cases and reduces the computational overhead associated with floating-point operations. By doing so, the algorithm simplifies table indexing: a single set of tables for layer widths and heights is generated, where the tables are shared across the positive and negative domains for the symmetric normal distribution, with absolute value handling for symmetry.[8] This approach retains the core ziggurat structure of sequential table lookups but optimizes for modern processors by minimizing branches and avoiding unnecessary comparisons like the original .[8] A key innovation in McFarland's version is the handling of the overhang regions, where samples fall outside the rectangular layers but within the density's "strips." For the normal distribution, the acceptance test in these overhangs is adapted to: if and , where represents the pre-scaled probability mass for the -th layer, enabling direct acceptance without additional scaling in most layers.[8] The tail beyond the first layer is managed through a transformation of exponentially distributed variates into the normal tail, avoiding complex fallback routines and leveraging the exponential generator's efficiency; this indirectly reduces rejection rates in tail sampling by integrating it seamlessly with the overall process.[8] For the exponential distribution, the modification integrates tail handling directly into the ziggurat framework without a separate recursive fallback, splitting overhangs into triangular sampling domains that approximate the density with linear bounds, thereby eliminating over 50% of the density function evaluations in those regions and achieving a 91% reduction in rejections for exponential overhangs.[8] These changes result in substantial performance gains over prior Ziggurat implementations, with speedups of 65% for exponential variates and 82% for normal variates compared to optimized C-language benchmarks.[8] The algorithm's efficiency stems from its table-driven nature, precomputed in 128-bit precision and rounded to 64-bit for storage, which further minimizes runtime computations.[8] McFarland's modification has been adopted in production software, notably as the default method for normal sampling in Java 17's RandomGenerator interface and subsequent versions, where it provides a fast, table-driven approach with rare computational fallbacks.[9] Compared to the original Ziggurat, it preserves the layered rejection sampling paradigm but enhances suitability for contemporary CPUs through fewer conditional branches and streamlined arithmetic.[8]Adaptations for Specific Distributions
The Ziggurat algorithm has been adapted for various distributions beyond the standard normal by adjusting the rectangle heights and widths to fit the target probability density function (PDF), while preserving the rejection sampling core. These adaptations typically involve solving for the boundaries that ensure equal areas under the stacked rectangles and scaling the heights to the minimum of the PDF over each interval. For distributions with heavier tails, more layers (rectangles) are employed to maintain efficiency, and tail handling often shifts to alternative methods like inverse cumulative distribution function (ICCDF) sampling to avoid excessive rejections.[10] The exponential distribution adaptation leverages its memoryless property, simplifying the process significantly. The PDF is for , and the Ziggurat tables are constructed such that the last rectangle's height exactly matches the exponential decay. When a sample falls in the tail beyond , it is generated as , where is a uniform random variate on (0,1), requiring no further rejection since the tail mirrors the distribution itself. This makes the exponential case nearly rejection-free, achieving acceptance rates over 99% with standard table sizes. McFarland's modification briefly references this handling for exponential tails in normal sampling contexts.[11] For the gamma distribution with PDF and shape , adaptations combine multiple exponential Ziggurats for integer shapes or adjust tables directly for general . The rectangle boundaries are solved iteratively using the incomplete gamma function to ensure the areas under the PDF segments align with the uniform proposal. Heights are set as . A 2024 extension adapts the Ziggurat for tail-adaptive generation from gamma-order normal distributions, including multivariate support.[10][12] The Cauchy distribution, with heavy tails and PDF , demands more layers—up to or higher—to cover the slowly decaying tails without prohibitive rejection rates. The central regions use standard Ziggurat rejection, but tails beyond the final rectangle fall back to ICCDF sampling, equivalent to transformations like for the standard case, ensuring exact generation. This adaptation achieves runtimes around 12 ns per variate with 4096 layers, outperforming alternatives by factors of 4–5.[10] A unique hybrid adaptation exists for the Student's t-distribution with degrees of freedom, applying Ziggurat sampling to the central portion where the PDF is well-approximated by rectangles, and switching to exact tail methods for values exceeding the outer layers. The central Ziggurat uses the t-PDF , with tails handled via inverse PDF (IPDF) or ICCDF to account for the polynomial decay. This approach yields acceptance efficiencies over 98% for moderate , with generation times as low as 12 ns for .[10]Applications and Implementations
Use in Statistical Software
The Ziggurat algorithm has been integrated into several major statistical software environments for efficient random number generation, particularly for normal and exponential distributions. In MATLAB, a variant of the algorithm based on Marsaglia and Tsang's 2000 method has been employed in therandn function since approximately 2000 to produce double-precision normally distributed random numbers, leveraging the underlying Mersenne Twister generator for uniformity. This provides improved performance over earlier methods like the polar Box-Muller transform.[13][6]
In the R programming language, the RcppZiggurat package provides high-speed implementations of both the original Ziggurat method from Marsaglia and Tsang (2000) and the improved version by Leong et al. (2005), enabling fast generation of standard normal random variates through C++ bindings integrated with R.
Java's JDK 17 and later versions incorporate McFarland's modification of the Ziggurat algorithm in the java.util.random.RandomGenerator interface, specifically within the nextGaussian() method for generating Gaussian random numbers, as part of the enhanced pseudorandom number generation framework introduced in JEP 356.
In Python, NumPy's random.normal function, when using the modern Generator class (available since NumPy 1.17), employs a 256-step Ziggurat method for normal distribution sampling, offering improved performance over earlier Box-Muller approaches. As of NumPy 2.x in 2025, this remains the default approach.[14] SciPy provides access to pure Ziggurat-based sampling through its integration with the UNU.RAN library in the scipy.stats module, supporting efficient generation for various distributions including the normal.[15]
The GNU Scientific Library (GSL), a widely used C library for numerical computations, has included Ziggurat implementations for the unit Gaussian and exponential distributions since the early 2000s, designating it as the fastest algorithm for these cases in its random number distribution functions. As of GSL 2.8 in 2025, this continues to hold.[16]
