views:

7963

answers:

10

How can I convert a uniform distribution (as most random number generators produce, e.g. between 0.0 and 1.0) into a normal distribution? What if I want a mean and standard deviation of my choosing?

A: 

Create and array with the distribution you require

e.g. 1,1,1,2,3,3,6,6,7,8,9,9,9,9,9,9,10

use a standard random number generator to select indexes into this array.

StocksR
+13  A: 

The Ziggurat algorithm is pretty efficient for this, although the Box-Muller transform is easier to implement from scratch (and not crazy slow).

Tyler
The usual warnings about linear congruent generators apply to both these methods, so use a decent underling generator. Cheers.
dmckee
Such as Mersenee Twister, or do you have other suggestions?
Gregg Lind
+1  A: 

Java's Random class has the nextGaussian() method for this.

Bill the Lizard
A: 

I would use Box-Muller. Two things about this:

  1. You end up with two values per iteration
    Typically, you cache one value and return the other. On the next call for a sample, you return the cached value.
  2. Box-Muller gives a Z-score
    You have to then scale the Z-score by the standard deviation and add the mean to get the full value in the normal distribution.
hughdbrown
How do you scale the Z-score?
Terhorst
scaled = mean + stdDev * zScore // gives you normal(mean,stdDev^2)
yoyoyoyosef
+2  A: 

The standard Python library module random has what you want:

normalvariate(mu, sigma)
Normal distribution. mu is the mean, and sigma is the standard deviation.

For the algorithm itself, take a look at the function in random.py in the Python library.

The manual entry is here

Brent.Longborough
A: 
function distRandom(){
  do{
    x=random(DISTRIBUTION_DOMAIN);
  }while(random(DISTRIBUTION_RANGE)>=distributionFunction(x));
  return x;
}
Not guaranteed to return, though, is it? ;-)
Peter K.
it returns almost surely.
Alexandre C.
+3  A: 

Changing the distribution of any function to another involves using the inverse of the probability function you want.

In other words, if you know the probability function p(x) and it has an inverse: Inv(p(x)) then by using the random probability function (uniform distribution) and casting the result value through the function Inv(p(x)) you should get random values cast with distribution according to the function you wanted, so now it's only a matter of choosing your desired probability function and its inverse.

Hope this helped and that I didn't mixed my math :)

Adi
+1 This is an overlooked method for generating gaussian variables which works very well. Inverse CDF can be efficiently computed with Newton method in this case (derivative is e^{-t^2}), an initial approximation is easy to get as a rational fraction, so you need 3-4 evaluations of erf and exp. It is mandatory if you use quasi-random numbers, a case where you must use exactly one uniform number to get a gaussian one.
Alexandre C.
+1  A: 

Use the central limit theorem wikipedia entry mathworld entry to your advantage.

Generate n of the uniformly distributed numbers, sum them, subtract n*0.5 and you have the output of an approximately normal distribution with mean equal to 0 and variance equal to (1/12) * (1/sqrt(N)) (see wikipedia on uniform distributions for that last one)

n=10 gives you something half decent fast. If you want something more than half decent go for tylers solution (as noted in the wikipedia entry on normal distributions)

jilles de wit
This won't give a particularly close normal (the "tails" or end-points will not be close to the real normal distribution). Box-Muller is better, as others have suggested.
Peter K.
Box Muller has wrong tails too (it returns a number between -6 and 6 in double precision)
Alexandre C.
+1  A: 

Here is a javascript implementation using the polar form of the Box-Muller transformation.

/*
 * Returns member of set with a given mean and standard deviation
 * mean: mean
 * standard deviation: std_dev 
 */
function createMemberInNormalDistribution(mean,std_dev){
    return mean + (gaussRandom()*std_dev);
}

/*
 * Returns random number in normal distribution centering on 0.
 * ~95% of numbers returned should fall between -2 and 2
 */
function gaussRandom() {
    var u = 2*Math.random()-1;
    var v = 2*Math.random()-1;
    var r = u*u + v*v;
    /*if outside interval [0,1] start over*/
    if(r == 0 || r > 1) return gaussRandom();

    var c = Math.sqrt(-2*Math.log(r)/r);
    return u*c;

    /* todo: optimize this algorithm by caching (v*c) 
     * and returning next time gaussRandom() is called.
     * left out for simplicity */
}
ouch (not-tail) recursion...
Alexandre C.
+1  A: 

There are plenty of methods:

  • Do not use Box Muller. Especially if you draw many gaussian numbers. Box Muller yields a result which is clamped between -6 and 6 (assuming double precision. Things worsen with floats.). And it is really less efficient than other available methods.
  • Ziggurat is fine, but needs a table lookup (and some platform-specific tweaking due to cache size issues)
  • Ratio-of-uniforms is my favorite, only a few addition/multiplications and a log 1/50th of the time (eg. look there).
  • Inverting the CDF is efficient (and overlooked, why ?), you have fast implementations of it available if you search google. It is mandatory for Quasi-Random numbers.
Alexandre C.