The Box—Muller transform holds a special place in my heart as it was the first method I ever had to implement for my own research.
The purpose of this transformation is simple. It takes a uniform (probably random) distribution and turns it into a Gaussian one.
That's it.
It was originally developed by George Box (yes, Box is his last name) and Mervin Muller in 1958 and is one of the most common methods to create a random, Gaussian distribution of points {{ "box1958" | cite }}. It's particularly useful when initializing a set of particles for a physical, N-body simulation. This chapter will be divided into a few subsections:
- How to initialize the Box—Muller transform
- How to use the Box—Muller transform in Cartesian coordinates {{ "box_muller_wiki" | cite }}.
- How to use the Box—Muller transform in Polar Coordinates, also called the Marsaglia transform {{ "marsaglia_wiki" | cite }}.
Of course, there will be full code examples at the bottom. So, let's get to it!
The main thing to mention here is that the Box—Muller transform requires some form of uniform distribution as its input. One obvious way to initialize a random distribution of points is to start with a grid, like so:
{% method %} {% sample lang="jl" %} import:3-32, lang:"julia" {% endmethod %}
This will create the following set of points for
To be honest, there are a bunch of ways to generate this exact same distribution.
Here, we simply walked backwards half of the grid size, determined the step size, and then placed a particle at each step.
Note that there is an inherent limitation with this method in that it only works for a square numbers.
Because of this, we decided to round
The real star of the show is the uniform random distribution, which can be generated like this:
{% method %} {% sample lang="jl" %} import:34-37, lang:"julia" {% endmethod %}
This will create the following set of points for
OK, but how do we know this is uniform? Good question!
The easiest way is to plot a histogram of a super large number of points.
If the random distribution is uniform, then all the bins should be roughly the same value.
The more points we have, the smaller the percent difference between the bins will be.
Here is a set of images generated by rand()
for
It is clear that the
Once this test is complete, we can be fairly sure that the function we are using to generate the initial distribution is uniform and ready for the next step of the process: actually using the Box—Muller transform!
The two dimensional Cartesian version of the Box—Muller transform starts with two random input values (
Looking at these equations,
So what about
From these values, we can calculate our new
Finally, in order to specify the size and shape of the generated Gaussian distribution, we can use the standard deviation,
In general, this can be written in code like so:
{% method %} {% sample lang="jl" %} import:41-49, lang:"julia" {% endmethod %}
Which produces the following output
Note that we have written the code to work on a single set of input values, but it could also be written to read in the entire distribution of points all at once. As this particular technique is usually implemented in parallel, it's up to you to decided which is the fastest for your own individual use-case.
At this stage, we have a good idea of how the transform works, but some people shy away from the Cartesian method in practice and instead opt for the polar form, which will be discussed next!
The Cartesian form of the Box—Muller transform is relatively intuitive.
The polar method is essentially the same, but without the costly
This means that we are essentially trying to transform our set of
From here, we can use basic trigonometric identities to redefine the
This changes the output equations to be
Again, the final values are:
In code, it might look like this:
{% method %} {% sample lang="jl" %} import:52-63, lang:"julia" {% endmethod %}
This will produce the following output:
Again, this is ultimately the same as the Cartesian method, except that it:
- Rejects points in the initial distribution that are outside of the unit circle (also called rejection sampling)
- Avoids costly
$$\sin$$ and$$\cos$$ operations
Point 2 means that the polar method should be way faster than the Cartesian one, but rejection sampling is somewhat interesting in it's own right, which we have discussed in a separate chapter
The example code here is straightforward: we start with a uniform distribution of points (both on a grid and a uniform random distribution) and then we preform the Box—Muller transform to see how far off it is from the Gaussian we expect.
{% method %} {% sample lang="jl" %} import, lang:"julia" {% endmethod %}
{% references %} {% endreferences %}
<script> MathJax.Hub.Queue(["Typeset",MathJax.Hub]); </script>The code examples are licensed under the MIT license (found in LICENSE.md).
The text of this chapter was written by James Schloss and is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
- The image "IFS triangle 1" was created by James Schloss and is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
- The image "IFS square 3" was created by James Schloss and is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
- The image "Simple Barnsley fern" was created by James Schloss and is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
- The video "Affine random transform 0" was created by James Schloss and is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
- The video "Affine random transform 1" was created by James Schloss and is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
- The video "Affine random transform 2" was created by James Schloss and is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
- The video "Affine random transform 3" was created by James Schloss and is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
- The video "Affine fern transform 0" was created by James Schloss and is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
- The video "Affine fern transform 1" was created by James Schloss and is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
- The video "Affine fern transform 2" was created by James Schloss and is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
- The video "Affine fern transform 3" was created by James Schloss and is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
- The video "Fern twiddle 0" was created by James Schloss and is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
- The video "Fern twiddle 1" was created by James Schloss and is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
- The video "Fern twiddle 2" was created by James Schloss and is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.
- The video "Fern twiddle 3" was created by James Schloss and is licensed under the Creative Commons Attribution-ShareAlike 4.0 International License.