Skip to content
Surf Wiki
Save to docs
general/monte-carlo-methods

From Surf Wiki (app.surf) — the open knowledge base

Marsaglia polar method

Method for generating pseudo-random numbers


Summary

Method for generating pseudo-random numbers

The Marsaglia polar method is a pseudo-random number sampling method for generating a pair of independent standard normal random variables.

Standard normal random variables are frequently used in computer science, computational statistics, and in particular, in applications of the Monte Carlo method.

The polar method works by choosing random points (x, y) in the square −1

: 0

and then returning the required pair of normal random variables as

: x\sqrt{\frac{-2\ln(s)}{s}},,\ \ y\sqrt{\frac{-2\ln(s)}{s}},

or, equivalently,

: \frac{x}{\sqrt{s}} \sqrt{-2\ln(s)},,\ \ \frac{y}{\sqrt{s}} \sqrt{-2\ln(s)},

where x/\sqrt{s} and y/\sqrt{s} represent the cosine and sine of the angle that the vector (x, y) makes with x axis.

Theoretical basis

The underlying theory may be summarized as follows:

If u is uniformly distributed in the interval 0 ≤ u (cos(2πu), sin(2πu)) is uniformly distributed on the unit circumference x2 + y2 = 1, and multiplying that point by an independent random variable ρ whose distribution is

:\Pr(\rho

will produce a point

: \left(\rho\cos(2\pi u),\rho\sin(2\pi u)\right)

whose coordinates are jointly distributed as two independent standard normal random variables.

History

This idea dates back to Laplace, whom Gauss credits with finding the above

:I=\int_{-\infty}^\infty e^{-x^2/2},dx

by taking the square root of

:I^2 = \int_{-\infty}^\infty\int_{-\infty}^\infty e^{-(x^2+y^2)/2},dx,dy =\int_0^{2\pi}\int_0^\infty re^{-r^2/2} , dr , d\theta.

The transformation to polar coordinates makes evident that θ is uniformly distributed (constant density) from 0 to 2π, and that the radial distance r has density

:re^{-r^2/2}. ,

(r2 has the appropriate chi square distribution.)

This method of producing a pair of independent standard normal variates by radially projecting a random point on the unit circumference to a distance given by the square root of a chi-square-2 variate is called the polar method for generating a pair of normal random variables,

Practical considerations

A direct application of this idea,

:x=\sqrt{-2\ln(u_1)}\cos(2\pi u_2),\quad y=\sqrt{-2\ln(u_1)}\sin(2\pi u_2)

is called the Box–Muller transform, in which the chi variate is usually generated as

:\sqrt{-2\ln(u_1)},

but that transform requires logarithm, square root, sine and cosine functions. On some processors, the cosine and sine of the same argument can be calculated in parallel using a single instruction. Notably for Intel-based machines, one can use fsincos assembler instruction or the expi instruction (available e.g. in D), to calculate complex

: \operatorname{expi}(z) = e^{i z} = \cos(z) + i \sin(z), ,

and just separate the real and imaginary parts.

Note: To explicitly calculate the complex-polar form use the following substitutions in the general form,

Let r = \sqrt{-2 \ln(u_1)} and z = 2 \pi u_2. Then

: \ re^{i z} = \sqrt{-2 \ln(u_1)} e^{i 2 \pi u_2} =\sqrt{-2 \ln(u_1)}\left[ \cos(2 \pi u_2) + i \sin(2 \pi u_2)\right].

In contrast, the polar method here removes the need to calculate a cosine and sine. Instead, by solving for a point on the unit circle, these two functions can be replaced with the x and y coordinates normalized to the \sqrt{x^2 + y^2} radius. In particular, a random point (x, y) inside the unit circle is projected onto the unit circumference by setting s=x^2+y^2 and forming the point

:\left( \frac{x}{\sqrt{s}}, \frac{y}{\sqrt{s}} \right), ,

which is a faster procedure than calculating the cosine and sine. Some researchers argue that the conditional if instruction (for rejecting a point outside of the unit circle), can make programs slower on modern processors equipped with pipelining and branch prediction.This effect can be heightened in a GPU generating many variates in parallel, where a rejection on one processor can slow down many other processors. See section 7 of {{citation | editor1-last = Chow | editor1-first = Paul | editor2-last = Cheung | editor2-first = Peter Y. K.

That random point on the circumference is then radially projected the required random distance by means of

:\sqrt{-2\ln(s)}, ,

using the same s because that s is independent of the random point on the circumference and is itself uniformly distributed from 0 to 1.

Implementation

Python

A simple implementation in Python:

PYTHON
import math
import random


def marsaglia_sample():
    while True:
        u1 = random.uniform(-1, 1)
        u2 = random.uniform(-1, 1)
        if (w := u1**2 + u2**2) < 1:
            break
    z1 = u1 * math.sqrt(-2 * math.log(w) / w)
    z2 = u2 * math.sqrt(-2 * math.log(w) / w)
    return z1, z2

Java

Simple implementation in Java using the mean and standard deviation:

JAVA
private static double spare;
private static boolean hasSpare = false;

public static synchronized double generateGaussian(double mean, double stdDev) {
    if (hasSpare) {
        hasSpare = false;
        return spare * stdDev + mean;
    } else {
        double u, v, s;
        do {
            u = Math.random() * 2 - 1;
            v = Math.random() * 2 - 1;
            s = u * u + v * v;
        } while (s >= 1 || s == 0);
        s = Math.sqrt(-2.0 * Math.log(s) / s);
        spare = v * s;
        hasSpare = true;
        return mean + stdDev * u * s;
    }
}

C++

A non-thread safe implementation in C++ using the mean and standard deviation:

C
double generateGaussian(double mean, double stdDev) {
    static double spare;
    static bool hasSpare = false;

    if (hasSpare) {
        hasSpare = false;
        return spare * stdDev + mean;
    } else {
        double u, v, s;
        do {
            u = (rand() / ((double)RAND_MAX)) * 2.0 - 1.0;
            v = (rand() / ((double)RAND_MAX)) * 2.0 - 1.0;
            s = u * u + v * v;
        } while (s >= 1.0 || s == 0.0);
        s = sqrt(-2.0 * log(s) / s);
        spare = v * s;
        hasSpare = true;
        return mean + stdDev * u * s;
    }
}

C++11 GNU GCC libstdc++'s implementation of std::normal_distribution uses the Marsaglia polar method, as quoted from herein.

Julia

A simple Julia implementation: """ marsagliasample(N)

Generate 2N samples from the standard normal distribution using the Marsaglia method. """ function marsagliasample(N) z = Array{Float64}(undef,N,2); for i in axes(z,1) s = Inf; while s 1 z[i,:] .= 2rand(2) .- 1; s = sum(abs2.(z[i,:])) end z[i,:] .*= sqrt(-2log(s)/s); end vec(z) end

""" marsagliasample(n,μ,σ)

Generate n samples from the normal distribution with mean μ and standard deviation σ using the Marsaglia method. """ function marsagliasample(n,μ,σ) μ .+ σ*marsagliasample(cld(n,2))[1:n]; end The for loop can be parallelized by using the Threads.@threads macro.

References

References

  1. (1964). "A Convenient Method for Generating Normal Variables". SIAM Review.
  2. Peter E. Kloeden Eckhard Platen Henri Schurz, Numerical Solution of SDE Through Computer Experiments, Springer, 1994.
  3. Kanter, David. (22 April 2012). "Intel's Ivy Bridge Graphics Architecture". Real World Tech.
Wikipedia Source

This article was imported from Wikipedia and is available under the Creative Commons Attribution-ShareAlike 4.0 License. Content has been adapted to SurfDoc format. Original contributors can be found on the article history page.

Want to explore this topic further?

Ask Mako anything about Marsaglia polar method — get instant answers, deeper analysis, and related topics.

Research with Mako

Free with your Surf account

Content sourced from Wikipedia, available under CC BY-SA 4.0.

This content may have been generated or modified by AI. CloudSurf Software LLC is not responsible for the accuracy, completeness, or reliability of AI-generated content. Always verify important information from primary sources.

Report