Posted on Saturday 12 February 2005
Keith Peters has done some experiments recently on randomness and it inspired me to give you a little theoretical background on the subject. One of the most impressive experiments uses a sum (Math.random() + Math.random() + ...)/n to create a distribution with more stuff in the centre (near 0.5) than on the sides. Why this works is quite interesting.
First a little digression on the subject of statistical mechanics and thermal physics. Admit that a certain particle can be in one of two energy states: 0 and . Then if you put a bunch of these particles in an isolated box, stat mech will tell you that the system will wander randomly through all of the particular states of the system. The system will spend more time in the more likely states, obviously. Say that you have only two particles. Then there is one way to get 0 energy,
, 2 ways to get
(
and
), and one way to get
(
) energy. So from simply adding two random particles, you get a more likely state. In fact, you can define the number of states with same energy to be omega, and then you get the famous Boltzmann formula:
. S is the entropy,
is the Boltzmann constant, and ln is the natural log (base e). Hence an energy level that can be generated by the large number of states has the greater entropy, and this is why (from a very rough, schematic stat mech point of view) "everything" (the universe as a closed system) tends toward a greater entropy, or in other terms, the second law of thermodynamics,
, holds.
Getting back to Keith's experiments, what he's doing is exactly like adding 'random particles' in a box. In this case there are or
states per 'particle' (Not quite sure of the randomness of Math.random() here, just guessing). Putting them in a 'box' (adding Math.random() several times then dividing by N), he gets more likely states near 0.5. In the limit of a large number of Math.random() calls, the distribution will stabilize toward the Gaussian bell curve, that is
, where scalar1 and scalar2 are arbitrary numbers determined by the distribution and exp is the exponential (e to the something).
Going back to Flash, if you wanted to have a Gaussian distribution instead of the usual Math.random() distribution, you could simply call scalar1 * Math.exp(-scalar2 * Math.pow((Math.random() - 0.5), 2)). The classic Gaussian distribution is generated by scalar1 = 1 and scalar2 = 24 (the gaussian distribution reaches almost 0 at x = ±3, hence scalar2 = 24 = 3 * 4 * 2). The trouble is that this is a bit expensive because of all that math, and it's not very flexible. Can we come up other distributions that will do the job?
Of course we can! What we have here is a starting range of 0 to 1, an end result in the same range, and we need a function from one to the other. Any bells ringing? That's right, easing functions. By applying any of Robert Penner's easing functions to Math.random() you can get a skewed distribution. The only issue here is that most of these easing functions have very little weight in the middle, and more on the sides, so getting a Gaussian-looking distribution might seem impossible. Not so. First apply the easing function to Math.random, getting a skewed distribution with more weight toward 0 and 1. Then either add 0.5 to the result if it's less than 0.5 or subtract 0.5 if it's more than 0.5. This will work for all of the easeInOut functions. Once you've figured out a distribution that you like, you can do some simple math analysis to reduce the number of math calls (for example, if you're using a sinusoidal distribution, you can modify the initial range of Math.random() by half a period to save a few CPU cycles). Here's some sample code to illustrate:
function addLines()
{
for(var i = 0; i < 10; i++)
{
var r = com.robertpenner.easing.Expo.easeInOut(Math.random(), 0,1,1);
if(r < 0.5)
{
r += 0.5;
}
else
{
r -= 0.5;
}
var a = _root.attachMovie('line', 'line' + i + '_' + 10*j, i + j*10);
a._x = r*500;
}
j++;
}
setInterval(addLines, 17);
var j = 0;
Useful? Probably not. Interesting? Hell yeah! (formatting of this document made possible with render plugin.)


