Box-Muller Transform

Quick-and-dirty visualization of the Polar Form of the Box-Muller transform.

by Steven Sell

HTML

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script>
<div id="holder">
</div>
  
<br />

Gaussian Distribution of 1000 random values using the Polar Form of the Box-Muller Transform.

<br /><br />

A performance comparison between the Basic and Polar forms may be found <a href="https://jsperf.com/box-mullerperformance">here</a>.

<br /><br />

X-Axis: Value <br/>
Y-Axis: Number of value occurences <br/>

<br/>

Min: <span id="minval"></span><br />
Max: <span id="maxval"></span><br />

CSS

#holder {
  width: 500px;
  height: 100px;
  border: 1px solid black;
  background-color: #FFFFFF;
}

.value {
  position: absolute;
  width: 2px;
  height: 2px;
  background-color: rgb(100, 155, 100);
}

#zero {
  position: absolute;
  height: 100px;
  width: 1px;
  background-color: rgb(255, 0, 0);
  font-size: 10px;
  color: rgb(255, 0, 0);
}

#zerotext {
  position: absolute; 
  bottom: 0;
  margin-left: 3px;
}

JavaScript

/**
 * Polar Form of the Box-Muller Transform for normally distributed PRNG generation.
 * See the following for a perfomance comparison between Basic and Polar forms:
 *
 *     http://jsperf.com/box-mullerperformance
 */
function gaussianRand() {

    this.generate = true;
    this.value0   = 0.0;
    this.value1   = 0.0;
    
    if(this.generate) {
      var x1 = 0.0;
      var x2 = 0.0;
      var w  = 0.0;

      do {
          // Math.random() gives value on range [0, 1) but
          // the Polar Form expects [-1, 1].
          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);

      this.value0 = x1 * w;
      this.value1 = x2 * w;

      result = this.value0;
  } else {
      result = this.value1;
  }

	this.generate = !this.generate
  return result;
}

function gaussianRandAdj(mean, stddev) {
     const value = gaussianRand();
     return ((value * stddev) + mean);
}

$(document).ready(function() {

    /**
     * Pardon the sloppiness. What happens below can be summed as:
     *
     *     1.  Generate a number of normally-distrubted random values.
     *     2.  Find the minimum and maximum generated value.
     *     3.  Transform the values on the range [0, width] to fit into our div.
     *     3b. At the same time, count how many times a value maps to [0, width] 
     *     4.  Find the index that occured the most so we can place vertically.
     *     5.  Create the value divs. The x-offset is it's index into finals, 
     *         the y-offset it's relative value compared to the most common index.
     *
     * Obviously, this is very hastily done and can be improved greatly.
     */
    
    //------------------------------------------------------------------
    // Step (1)
    //------------------------------------------------------------------
     
    var count  = 1000;     // # of gaussian values to...