72
votes

I want to know if the JavaScript function Math.random uses a normal (vs. uniform) distribution or not.

If not, how can I get numbers which use a normal distribution? I haven't found a clear answer on the Internet, for an algorithm to create random normally-distributed numbers.

I want to rebuild a Schmidt-machine (German physicist). The machine produces random numbers of 0 or 1, and they have to be normally-distributed so that I can draw them as a Gaussian bell curve.

For example, the random function produces 120 numbers (0 or 1) and the average (mean) of these summed values has to be near 60.

19
Not an answer (because it wasn't your question :P), but this should help: meredithdodge.com/2012/05/30/…Jongware
averaging a few Math.random() already gives you a normal-ish distribution with even a little few calls like 3. see here jsbin.com/tetizotugu/1/edit?js,output modify the first parameter.GameAlchemist
this is a very common confusion, since statistics is taught as if the normal curve is the most important thing before people can understand what it is. If you were to take 30 averages(sum xs / length xs) of 30 coins per sample. these 30 averages would be approximately normally distributed, and as you keep increasing 30 to larger numbers, it will converge towards perfect normal curve centered around 0.5. random itself is uniform int distribution(with parameters n, n + 1 usually 0, 1), which is how it's known as in mersanne twister random engine mt19937 in c++.Dmitry
The question itself is valuable, as asking for normal dist random numbers. About OP use case, he was not able to explain it very well as Dmirty..Andre Figueiredo

19 Answers

60
votes

I want to know if the JavaScript function Math.random is normal distribution or not

Javascript Math.random is not a Normal Distribution(Gaussian bell curve). From ES 2015, 20.2.2.27 "Returns a Number value with positive sign, greater than or equal to 0 but less than 1, chosen randomly or pseudo randomly with approximately uniform distribution over that range, using an implementation-dependent algorithm or strategy. This function takes no arguments." So the provided collection when n is high enough we will get approximately uniform distribution. All values in the interval will have equal probability of appearance(straight line parallel to the x axis, denoting number between 0.0 and 1.0).

how can I get numbers which are normal distribution

There are several ways of getting collection of numbers with a normal distribution. As answered by Maxwell Collard the Box-Muller transform does transform uniform distribution to normal distribution(the code can be found in Maxwell Collard answer).

An answer to another stackoverflow answer to a question has a reply with other uniform distribution to normal distribution algorithms. Such as: Ziggurat, Ratio-of-uniforms, Inverting the CDF Besides one of the answers says that: says:

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

And finally

I want to rebuilt a Schmidt-machine (German physicist), the machine produces random numbers of 0 or 1 and they have to be normal distributed so I can draw them in Gaussian bell curve.

When we have only two values (0 or 1) Gaussian curve looks the same as uniform distribution with 2 possible values. That is why a simple

function randomZero_One(){
    return Math.round(Math.random());
}

would suffice. It would return pseudo-randomly with approximately equal probability values 0 and 1.

142
votes

Since this is the first Google result for "js gaussian random" in my experience, I feel an obligation to give an actual answer to that query.

The Box-Muller transform converts two independent uniform variates on (0, 1) into two standard Gaussian variates (mean 0, variance 1). This probably isn't very performant because of the sqrt, log, and cos calls, but this method is superior to the central limit theorem approaches (summing N uniform variates) because it doesn't restrict the output to the bounded range (-N/2, N/2). It's also really simple:

// Standard Normal variate using Box-Muller transform.
function randn_bm() {
    var u = 0, v = 0;
    while(u === 0) u = Math.random(); //Converting [0,1) to (0,1)
    while(v === 0) v = Math.random();
    return Math.sqrt( -2.0 * Math.log( u ) ) * Math.cos( 2.0 * Math.PI * v );
}
120
votes

Normal Distribution Between 0 and 1

Building on Maxwell's Answer, this code uses the Box–Muller transform to give you a normal distribution between 0 and 1 inclusive. It just resamples the values if it's more than 3.6 standard deviations away (less than 0.02% chance).

function randn_bm() {
  let u = 0, v = 0;
  while(u === 0) u = Math.random(); //Converting [0,1) to (0,1)
  while(v === 0) v = Math.random();
  let num = Math.sqrt( -2.0 * Math.log( u ) ) * Math.cos( 2.0 * Math.PI * v );
  num = num / 10.0 + 0.5; // Translate to 0 -> 1
  if (num > 1 || num < 0) return randn_bm() // resample between 0 and 1
  return num
}

Visualizations

enter image description here

n = 100

enter image description here

n = 10,000

enter image description here

n = 10,000,000

Normal Distribution With Min, Max, Skew

This version allows you to give a min, max, and skew factor. See my usage examples at the bottom.

function randn_bm(min, max, skew) {
  let u = 0, v = 0;
  while(u === 0) u = Math.random() //Converting [0,1) to (0,1)
  while(v === 0) v = Math.random()
  let num = Math.sqrt( -2.0 * Math.log( u ) ) * Math.cos( 2.0 * Math.PI * v )
  
  num = num / 10.0 + 0.5 // Translate to 0 -> 1
  if (num > 1 || num < 0) 
    num = randn_bm(min, max, skew) // resample between 0 and 1 if out of range
  
  else{
    num = Math.pow(num, skew) // Skew
    num *= max - min // Stretch to fill range
    num += min // offset to min
  }
  return num
}

enter image description here

randn_bm(-500, 1000, 1);

enter image description here

randn_bm(10, 20, 0.25);

enter image description here

randn_bm(10, 20, 3);

Here is the JSFiddle for these screenshots: https://jsfiddle.net/2uc346hp/

48
votes

I wanted to have approximately gaussian random numbers between 0 and 1, and after many tests I found this to be the best:

function gaussianRand() {
  var rand = 0;

  for (var i = 0; i < 6; i += 1) {
    rand += Math.random();
  }

  return rand / 6;
}

And as a bonus:

function gaussianRandom(start, end) {
  return Math.floor(start + gaussianRand() * (end - start + 1));
}
18
votes

The Javascript Math.random() pseudorandom function returns variates that are equally distributed between 0 and 1. To get a Gaussian distribution I use this:

// returns a gaussian random function with the given mean and stdev.
function gaussian(mean, stdev) {
  var y2;
  var use_last = false;
  return function() {
    var y1;
    if (use_last) {
      y1 = y2;
      use_last = false;
    } else {
      var x1, x2, w;
      do {
        x1 = 2.0 * Math.random() - 1.0;
        x2 = 2.0 * Math.random() - 1.0;
        w = x1 * x1 + x2 * x2;
      } while (w >= 1.0);
      w = Math.sqrt((-2.0 * Math.log(w)) / w);
      y1 = x1 * w;
      y2 = x2 * w;
      use_last = true;
    }

    var retval = mean + stdev * y1;
    if (retval > 0)
      return retval;
    return -retval;
  }
}

// make a standard gaussian variable.     
var standard = gaussian(100, 15);

// make a bunch of standard variates
for (i = 0; i < 1000; i++) {
  console.log( standard() )
}

I think I got this from Knuth.

Plot can be seen here

13
votes

Function that utilises the central limit theorem.

function normal(mu, sigma, nsamples){
    if(!nsamples) nsamples = 6
    if(!sigma) sigma = 1
    if(!mu) mu=0

    var run_total = 0
    for(var i=0 ; i<nsamples ; i++){
       run_total += Math.random()
    }

    return sigma*(run_total - nsamples/2)/(nsamples/2) + mu
}
10
votes

From the spec:

15.8.2.14 random ( )

Returns a Number value with positive sign, greater than or equal to 0 but less than 1, chosen randomly or pseudo randomly with approximately uniform distribution over that range, using an implementation-dependent algorithm or strategy. This function takes no arguments.

So, it's a uniform distribution, not normal or Gaussian. That's what you're going to find in just about any standard random number facility in any basic language runtime outside of specialized statistics libraries.

9
votes

You are confusing the output of the function (which is a uniform distribution between 0 and 1) with the need to generate a Gaussian distribution by repeatedly drawing random numbers that are either 0 or 1 - after a large number of trials, their sum will be approximately normally distributed.

You can use the Math.random() function, then round the result to an integer: if it's < 0.5, return 0; if its >= 0.5, return 1. Now you have equal probabilities of zero and one, and you can continue with the approach you described in your question.

Just to clarify: I don't think it's possible to have an algorithm that produces either 0's or 1's in a normally distributed way - normal distribution requires a continuous variable.

When you do the above for say 120 numbers, you will on average get 60 1's and 60 0's. The actual distribution you get will be the binomial distribution with a mean of 60 and a standard deviation of

stdev = sqrt(p(1-p)N) = 5.48

The probability of a particular number k when you have n samples with probability p (which we fixed at 0.5) is

p = n! / ((n-k)! k!) p^k (1-p)^(n-k)

When p = 0.5, you end up with just the binomial coefficients - which approach the normal distribution for n > 30, typically.

6
votes

And a single line example:

Math.sqrt(-2 * Math.log(Math.random()))*Math.cos((2*Math.PI) * Math.random())

and a Fiddle https://jsfiddle.net/rszgjqf8/

5
votes

For those interested in generating values of a normal distrubution, I would recommend checking this implementation of the Ziggurat algorithm in JavaScript: https://www.npmjs.com/package/node-ziggurat

The code of found in the author's page is:

function Ziggurat(){

var jsr = 123456789;

var wn = Array(128);
var fn = Array(128);
var kn = Array(128);

function RNOR(){
  var hz = SHR3();
  var iz = hz & 127;
  return (Math.abs(hz) < kn[iz]) ? hz * wn[iz] : nfix(hz, iz);
}

this.nextGaussian = function(){
  return RNOR();
}

function nfix(hz, iz){
  var r = 3.442619855899;
  var r1 = 1.0 / r;
  var x;
  var y;
  while(true){
    x = hz * wn[iz];
    if( iz == 0 ){
      x = (-Math.log(UNI()) * r1); 
      y = -Math.log(UNI());
      while( y + y < x * x){
        x = (-Math.log(UNI()) * r1); 
        y = -Math.log(UNI());
      }
      return ( hz > 0 ) ? r+x : -r-x;
    }

    if( fn[iz] + UNI() * (fn[iz-1] - fn[iz]) < Math.exp(-0.5 * x * x) ){
      return x;
    }
    hz = SHR3();
    iz = hz & 127;

    if( Math.abs(hz) < kn[iz]){
      return (hz * wn[iz]);
    }
  }
}

function SHR3(){
  var jz = jsr;
  var jzr = jsr;
  jzr ^= (jzr << 13);
  jzr ^= (jzr >>> 17);
  jzr ^= (jzr << 5);
  jsr = jzr;
  return (jz+jzr) | 0;
}

function UNI(){
  return 0.5 * (1 + SHR3() / -Math.pow(2,31));
}

function zigset(){
  // seed generator based on current time
  jsr ^= new Date().getTime();

  var m1 = 2147483648.0;
  var dn = 3.442619855899;
  var tn = dn;
  var vn = 9.91256303526217e-3;

  var q = vn / Math.exp(-0.5 * dn * dn);
  kn[0] = Math.floor((dn/q)*m1);
  kn[1] = 0;

  wn[0] = q / m1;
  wn[127] = dn / m1;

  fn[0] = 1.0;
  fn[127] = Math.exp(-0.5 * dn * dn);

  for(var i = 126; i >= 1; i--){
    dn = Math.sqrt(-2.0 * Math.log( vn / dn + Math.exp( -0.5 * dn * dn)));
    kn[i+1] = Math.floor((dn/tn)*m1);
    tn = dn;
    fn[i] = Math.exp(-0.5 * dn * dn);
    wn[i] = dn / m1;
  }
}
zigset();
}

Create a Ziggurat.js file and then:

var z = new Ziggurat();
z.nextGaussian();

For me it's working just perfect and as I had read in Wikipedia, this is a more efficient algorithm than the Box-Muller.

enter link description here

3
votes

I have tested several functions with the right configuration all work similarly and well.

http://jsfiddle.net/p3y40gf3/29/

Central limit is nice, must be with (n=3 for 6) and 12 for 12 to look as others. I configured others also to (6) or 12 or 1/12 as standard deviation, not sure why 12.

Central limit is a tiny bit less centered than Box/Muller and Ziggurat.

Box/Muller and Ziggurat look exactly the same

this variant by Joe(https://stackoverflow.com/a/33567961/466363) does standard deviation correctly:

function normal(mu, sigma, nsamples){ // using central limit
    if(!nsamples) nsamples = 3
    if(!sigma) sigma = 1
    if(!mu) mu=0

    var run_total = 0
    for(var i=0 ; i<nsamples ; i++){
       run_total += Math.random()
    }

    return sigma*(run_total - nsamples/2)/(nsamples/2) + mu
}

Ziggurat is also nice but needs to be adjusted from z score to from 0 to 1 looks like it makes good numbers.

Box/Muller clipped is good but gives few repeated numbers at clipped edges but it is very similar to others, incorrect random numbers should be discarded not clipped.

function randn_bm() {
    var u = 0, v = 0;
    while(u === 0) u = Math.random(); //Converting [0,1) to (0,1)
    while(v === 0) v = Math.random();
    let num = Math.sqrt( -2.0 * Math.log( u ) ) * Math.cos( 2.0 * Math.PI * v );
    num = num / 6.0 + 0.5; // Translate to 0 -> 1 // changed here 10 to 6
    if(num>1||num<0) return randn_bm(); return num; // bad random numbers should be discared not clipped
    //return Math.max(Math.min(num, 1), 0); // cap between 0 and 1
}

Central limit variant it is called Bates distribution that is average https://en.wikipedia.org/wiki/Bates_distribution

not confused with Irwin Hall that is a sum https://en.wikipedia.org/wiki/Irwin%E2%80%93Hall_distribution

https://en.wikipedia.org/wiki/Normal_distribution#Generating_values_from_normal_distribution

2
votes

A non verbose function to sample a random value from a Gaussian distribution I wrote some time ago:

function gaussianRandom(mean, sigma) {
  let u = Math.random()*0.682;
  return ((u % 1e-8 > 5e-9 ? 1 : -1) * (Math.sqrt(-Math.log(Math.max(1e-9, u)))-0.618))*1.618 * sigma + mean;
}

It should work if you clamp the values to the range you want.

1
votes

This is my solution to the problem, using the Marsaglia polar method. The range depends on the parameters you give, without parameters it almost never generates anything outside of the range.

As it generates two normally distributed numbers per iteration, I declared a variable under window.temp.spareNormal to grab the spare one if it's there. Might not be the best location for it, but hey.

You'd probably have to round the result in order to get what you want.

window.temp = {
    spareNormal: undefined
};

Math.normal = function (mean, standardDeviation) {
    let q, u, v, p;

    mean = mean || 0.5;
    standardDeviation = standardDeviation || 0.125;

    if (typeof temp.spareNormal !== 'undefined') {
        v = mean + standardDeviation * temp.spareNormal;
        temp.spareNormal = undefined;

        return v;
    }

    do  {
        u = 2.0 * Math.random() - 1.0;
        v = 2.0 * Math.random() - 1.0;

        q = u * u + v * v;
    } while (q >= 1.0 || q === 0);

    p = Math.sqrt(-2.0 * Math.log(q) / q);

    temp.spareNormal = v * p;
    return mean + standardDeviation * u * p;
}
0
votes

I found this library that includes lots of useful Random functions. You can either install it via simjs from npm, or just take the random-node-*.js file out directly for what you need.

http://www.simjs.com/random.html http://www.simjs.com/download.html

0
votes

for finding normal distribution of value:

getNormal = (x, mean, standardDeviation, ) => {
 return (1 / standardDeviation * Math.sqrt(2 * (3, 14))) * Math.pow(Math.E, -Math.pow(x - mean, 2) / (2 * (standardDeviation * standardDeviation)));  
}
0
votes

The only sort of qualifications I have for this is having taken a single statistics class. If I get something wrong, please tell me, I'd like to learn more about statistics and I don't want to keep thinking something wrong.

If you want to create a random number generator that produces numbers in a normal distribution, you should be able to take samples from a uniform distribution, which is no problem. If you set up a basic random number generator that generates numbers in range a to b, the distribution of values produced will have µ = (a+b)/2 and σ = (b-a)/√12. If the mean of a a few sample of values (≥30) taken from this distribution is taken for many such samples, then for the sampling distribution µ (sample means) = µ (population mean) and σ (sample means' stdev) = σ (population stdev)/√n (number of values in the sample).

By controlling the mean and stdev of the original distribution, you can control the ending mean and standard deviation of a random number generator that produces a normal distribution.

function all_normal(mu, sigma, nsamp)
{
    var total = 0;
    for (var a = 0; a < nsamp; a ++)
    {
       total += rand_int(mu - (sigma * Math.sqrt(3 * nsamp)), mu + (sigma * Math.sqrt(3 * nsamp)));
    }
    return Math.ceil(total / nsamp);
}
0
votes

This is my JavaScript implementation of Algorithm P (Polar method for normal deviates) from Section 3.4.1 of Donald Knuth's book The Art of Computer Programming:

function gaussian(mean, stddev) {
    return function() {
        var V1
        var V2
        var S
        do{
            var U1 = Math.random()
            var U2 = Math.random()
            V1 = 2*U1-1
            V2 = 2*U2-1
            S = V1*V1+V2*V2
        }while(S >= 1)
        if(S===0) return 0
        return mean+stddev*(V1*Math.sqrt(-2*Math.log(S)/S))
    }
} 

Use it like that:

var standard_normal = gaussian(0,1)
var a_standard_normal_deviate = standard_normal()
-1
votes
let iset = 0;
let gset;

function randn() {

   let v1, v2, fac, rsq;

   if (iset == 0) {
   do {
     v1 = 2.0*Math.random() - 1.0;
     v2 = 2.0*Math.random() - 1.0;
     rsq = v1*v1+v2*v2;
   } while ((rsq >= 1.0) || (rsq == 0));
   fac = Math.sqrt(-2.0*Math.log(rsq)/rsq);
   gset = v1*fac;
   iset = 1;
   return v2*fac;
 } else {
   iset = 0;
   return gset;
 }

}
-3
votes
//This is what I use for a Normal-ish distribution random function.
function normal_random(){
  var pos = [ Math.random(), Math.random() ];
  while ( Math.sin( pos[0] * Math.PI ) > pos[1] ){
    pos = [ Math.random(), Math.random() ];
  }   
  return pos[0];
};

This function returns a value between 0 and 1. Values near 0.5 are returned most often.