# Box-Muller transform (C)

The Box-Muller transform is a simple method for generating normally distributed random numbers from uniformly distributed random numbers. Since most pseudorandom number generators output uniformly or nearly-uniformly distributed values, it is commonly used in combination with these to create generators for normally distributed values, which are useful in simulating data sets with a known mean (average) and spread.

Suppose our random number generator is `rand()`

and returns an integer between 0 and `RAND_MAX`

, inclusive; any uniform random number generator will suffice. We produce two random integers and scale them to floating-point numbers between −1 and 1:

<<scale two random integers to doubles between -1 and 1>>=x = 2.0*rand()/RAND_MAX - 1; y = 2.0*rand()/RAND_MAX - 1;

To use the standard library's `rand()`

we add stdlib.h:

<<header files>>=#include <stdlib.h>

In order to use the polar form of the Box-Muller transform, it is necessary that the point (*x*,*y*) falls inside the unit circle (the circle of radius 1 about the origin), and not at the origin. To ensure this, we simply reject any pairs that don't meet these criteria and try again:

<<choose a point x,y in the unit circle uniformly at random>>=doublex, y, r;do{scale two random integers to doubles between -1 and 1 r = x*x + y*y;}while(r == 0.0 || r > 1.0);

Above, `r`

is just the square of the Euclidean distance of the point (*x*,*y*) from the origin (0,0). We don't need to take the square root because if the square falls between 0 and 1, so will the distance, and likewise if it is greater than 1 (this is a property of the square root function).

Next, we compute the desired normally distributed value pair using the Box Muller transform:

<<Apply Box-Muller transform on x, y>>=doubled = sqrt(-2.0*log(r)/r);doublen1 = x*d; n2 = y*d;

This is the most complex part to understand. Intuitively, it maps each circle of points around the origin to another circle of points around the origin, as shown in the diagram, where larger outer circles are mapped to closely-spaced inner circles and inner circles to outer circles. The `sqrt()`

and `log()`

functions require a header:

<<header files>>=#include <math.h>

These points will be distributed with a mean of 0 and a standard deviation of 1. To achieve any mean and standard deviation, we simply scale and translate:

<<scale and translate to get desired mean and standard deviation>>=doubleresult = n1*stddev + mean;

Some applications may want both values returned; however, since most will want only a single value at a time, we simply return one of the values and cache the other one for the next call, like this:

<<complete Box-Muller function>>=doublerand_normal(doublemean,doublestddev){staticdoublen2 = 0.0;staticintn2_cached = 0;if(!n2_cached){choose a point x,y in the unit circle uniformly at random{Apply Box-Muller transform on x, y scale and translate to get desired mean and standard deviation n2_cached = 1;returnresult;}}else{n2_cached = 0;returnn2*stddev + mean;}}

Alternatively we could just drop the second value, but the `sqrt`

and `log`

values are expensive enough that it's worth calling them less often.

We can now call the code with a simple test driver like this:

<<box_muller.c>>=header files#include <time.h>#include <stdio.h>complete Box-Muller functionintmain(){intbuckets[20] ={0};inti, j; srand(time(NULL));for(i=0; i<500; i++){doubleval = rand_normal(10.0, 3.0);inti = (int)floor(val + 0.5);if(i >= 0 && i <sizeof(buckets)/sizeof(*buckets)) buckets[i]++;}for(i=0; i<sizeof(buckets)/sizeof(*buckets); i++){for(j=0; j<buckets[i]; j++){printf("#");}printf("\n");}return0;}

The test driver creates a simple histogram of the output values by incrementing the "bucket" that each value falls into. After doing enough of these, the shape should appear roughly like a normal curve. Here's some sample output:

# ### ############ ################ ######################## ################################################## ########################################################### ############################################################## #################################################################### ########################################################## ############################################### ############################################ ######################## ################# ######## ###### #

Download code |