ExciteMike.com - Games by Mike Meyer ExciteMike.com - Games by Mike Meyer

Contents

Random Numbers with the Distribution You Want

The Problem

I was playing around with game prototyping and when the user tapped the touchscreen (I was messing around with DS Homebrew), I wanted to spawn a bunch of particles distributed randomly within some radius around where he tapped. So my first approach was to take my existing random number generator to get a value for my offset, then take a random direction to apply that offset in and I would have my point.

Now most random number generators you'll find try to give you a value from 0 to some maximum, with the chance for any one number in that range being as close as possible to the chance for any other number. From here on out I'm going to be assuming that all possible values of our random number generator come up with equal probability, and to make things easier, I'm going to sort of "normalize" the range and treat it as though these values came back in the range LaTex: [0,1) --- http://meta.wikimedia.org/wiki/LaTeX_on_a_shared_host instead of whatever range they actually fall in.

So we will get random numbers along the offset looking something like this:

Image:Prob_img_00.PNG
(100 faded-out dots distributed along a line segment with such a distribution)

And if we rotate that offset a random amount around the point the user tapped, we get them distributed something like this:

Image:Prob_img_01.PNG
(400 faded-out dots distributed within a circle this way)

But we have something undesirable happening here. The points are much more crowded together in the center of the circle. We would want them distributed more evenly; something more like this:

Image:Prob_img_02.PNG
(400 faded-out dots distributed using the tricks described here)

What Do We Want?

Let's get into some math to get a better idea of what's happening here. Before we rotated them, we got our points at any radius LaTex: r --- http://meta.wikimedia.org/wiki/LaTeX_on_a_shared_host distributed pretty evenly, but something happened when we rotated. For each value of LaTex: r --- http://meta.wikimedia.org/wiki/LaTeX_on_a_shared_host, the points that (potentially) ended up there were now spread around a circle with perimeter LaTex: 2 \pi r --- http://meta.wikimedia.org/wiki/LaTeX_on_a_shared_host. Since the perimeter increases in direct proportion to the offset distance, we want the "density" of our random values points at a radius LaTex: r --- http://meta.wikimedia.org/wiki/LaTeX_on_a_shared_host to also be in direct proportion to LaTex: r --- http://meta.wikimedia.org/wiki/LaTeX_on_a_shared_host.

So far I've been talking about the "distribution" and "density" of random numbers, but I should be more precise about what I mean. There is a concept in statistics used for exactly for what I mean here, called Probability Density. Statistics.com has a pretty good summary of the concept here, and Wikipedia goes a bit more in-depth here. The core of the idea is that for some continuous random variable LaTex: x --- http://meta.wikimedia.org/wiki/LaTeX_on_a_shared_host and its probability density function LaTex: p --- http://meta.wikimedia.org/wiki/LaTeX_on_a_shared_host, the probability that LaTex: x --- http://meta.wikimedia.org/wiki/LaTeX_on_a_shared_host lies between some LaTex: A --- http://meta.wikimedia.org/wiki/LaTeX_on_a_shared_host and LaTex: B --- http://meta.wikimedia.org/wiki/LaTeX_on_a_shared_host is LaTex: \int_{A}^{B}{p(x)\,dx} --- http://meta.wikimedia.org/wiki/LaTeX_on_a_shared_host.

As previously mentioned, we want to choose our random offset from the point tapped so that the "density" increases in direct proportion to the offset. For example, we want to get values near LaTex: \frac{2}{3} --- http://meta.wikimedia.org/wiki/LaTeX_on_a_shared_host twice as often as values near LaTex: \frac{1}{3} --- http://meta.wikimedia.org/wiki/LaTeX_on_a_shared_host. We can now state that as

LaTex: C p(x) = p(C x) --- http://meta.wikimedia.org/wiki/LaTeX_on_a_shared_host.

So a very easy solution for LaTex: p(x) --- http://meta.wikimedia.org/wiki/LaTeX_on_a_shared_host is LaTex: p(x) = C x --- http://meta.wikimedia.org/wiki/LaTeX_on_a_shared_host. But what is LaTex: C --- http://meta.wikimedia.org/wiki/LaTeX_on_a_shared_host? Well, since we're dealing with numbers between one and zero, the probability of getting a number in that range needs to be one. This means that

LaTex: \begin{array}{lcl}
\int_{0}^{1}p(x)\,dx &=& 1\\
\int_{0}^{1}C x\,dx &=& 1\\
\frac{1}{2} C (1)^2 + \frac{1}{2} C (0)^2 &=& 1\\
\frac{C}{2} &=& 1\\
C &=& 2\\
p(x) &=& 2x
\end{array}
</p>
<pre>--- http://meta.wikimedia.org/wiki/LaTeX_on_a_shared_host

How To Get Numbers With That Probability Density

Now we know what the probability density should be, but how do we get random numbers matching that distribution? The solution I am going to present is to find some function to apply to the numbers we get back from our standard random number generators, so that the result will have the probability density we desire. I'm going to do this the hard way and work out the more general case first, then apply it here.

Transforming From One Probability Distribution To Another

In the more general problem, we are sampling some continuous random variable in the range LaTex: [0,1) --- http://meta.wikimedia.org/wiki/LaTeX_on_a_shared_host with a probability density function of LaTex: p_0 --- http://meta.wikimedia.org/wiki/LaTeX_on_a_shared_host. We are looking for a transformation LaTex: T --- http://meta.wikimedia.org/wiki/LaTeX_on_a_shared_host we can apply to those numbers such that the resulting set of values is in LaTex: [0,1) --- http://meta.wikimedia.org/wiki/LaTeX_on_a_shared_host and has probability density LaTex: p_1 --- http://meta.wikimedia.org/wiki/LaTeX_on_a_shared_host.

So for any range LaTex: [A,B) --- http://meta.wikimedia.org/wiki/LaTeX_on_a_shared_host within LaTex: [0,1) --- http://meta.wikimedia.org/wiki/LaTeX_on_a_shared_host we are getting random values in that range with probability LaTex: \int_{A}^{B} p_0(u)\, du --- http://meta.wikimedia.org/wiki/LaTeX_on_a_shared_host and we are looking for a function LaTex: T --- http://meta.wikimedia.org/wiki/LaTeX_on_a_shared_host that will transform our random values so that the probability of the transformed value lying between LaTex: A --- http://meta.wikimedia.org/wiki/LaTeX_on_a_shared_host and LaTex: B --- http://meta.wikimedia.org/wiki/LaTeX_on_a_shared_host is LaTex: \int_{A}^{B} p_1(u)\, du --- http://meta.wikimedia.org/wiki/LaTeX_on_a_shared_host. The range that LaTex: T --- http://meta.wikimedia.org/wiki/LaTeX_on_a_shared_host will map to LaTex: [A,B) --- http://meta.wikimedia.org/wiki/LaTeX_on_a_shared_host is LaTex: \left[T^{-1}(A),T^{-1}(B)\right) --- http://meta.wikimedia.org/wiki/LaTeX_on_a_shared_host.

Putting it together now, the two functions over the two ranges should give the same probability, so now we have our equation from which we can deduce LaTex: T --- http://meta.wikimedia.org/wiki/LaTeX_on_a_shared_host:
LaTex: \int_{A}^{B} p_1(u)\, du = \int_{T^{-1}(A)}^{T^{-1}(B)} p_0(u)\, du  --- http://meta.wikimedia.org/wiki/LaTeX_on_a_shared_host

Applying This To Our Problem

In the particular problem we are working on, LaTex: p_0(x) = 1 --- http://meta.wikimedia.org/wiki/LaTeX_on_a_shared_host. (The "source" random values are distributed evenly) So we can substitute that in to the equation we just found:

LaTex: \begin{array}{lcl}
\int_{A}^{B} p_1(u)\, du &=& \int_{T^{-1}(A)}^{T^{-1}(B)} p_0(u)\, du \\
\int_{A}^{B} p_1(u)\, du &=& \int_{T^{-1}(A)}^{T^{-1}(B)}\, du\\
\int_{A}^{B} p_1(u)\, du &=& T^{-1}(B) - T^{-1}(A)
\end{array} --- http://meta.wikimedia.org/wiki/LaTeX_on_a_shared_host

which means the derivative of LaTex: T^{-1} --- http://meta.wikimedia.org/wiki/LaTeX_on_a_shared_host is LaTex: p_1 --- http://meta.wikimedia.org/wiki/LaTeX_on_a_shared_host!

And we determined above that the probability density we want is LaTex: p_1(x) = 2x --- http://meta.wikimedia.org/wiki/LaTeX_on_a_shared_host, so let's substitute that in.

LaTex: \begin{array}{lcl}
\int_{A}^{B} 2u\, du &=& T^{-1}(B) - T^{-1}(A)\\
B^2 - A^2 &=& T^{-1}(B) - T^{-1}(A)
\end{array} --- http://meta.wikimedia.org/wiki/LaTeX_on_a_shared_host

So LaTex: T^{-1}(x) = x^2 --- http://meta.wikimedia.org/wiki/LaTeX_on_a_shared_host works for us, and we see that LaTex: T(x) = \pm\sqrt(x) --- http://meta.wikimedia.org/wiki/LaTeX_on_a_shared_host.
We want the positive square root because it needs to map LaTex: [0,1) --- http://meta.wikimedia.org/wiki/LaTeX_on_a_shared_host onto LaTex: [0,1) --- http://meta.wikimedia.org/wiki/LaTeX_on_a_shared_host.

The End

So to get the sprites distributed the way we want, we can do (pseudocode)

radius = sqrt( rand() / RAND_MAX ) * MAX_RADIUS;
angle = rand() * 2 * PI / RAND_MAX;

(and pretend this is floating point so that dividing by RAND_MAX doesn't screw you over)

So now it distributes the sprites like this:

Image:Prob_img_03.PNG
(1000 dots distributed throughout a circle. Dots are 30% opaque.)

Here's the actual actionscript used to generate this last image in Flash:
(of course, you'd need an mcCircle on the stage and a movieclip in the library with the linkage "Dot")

var centerX:Number = mcCircle._x;
var centerY:Number = mcCircle._y;
var maxOffset:Number = mcCircle._width / 2;

var NUM_DOTS:Number = 1000;
for (var i:Number = 0; i<NUM_DOTS; ++i)
{
	var Dot:MovieClip = this.attachMovie("Dot", "Dot" + i, 100+i);
	var angle:Number = Math.random() * 2 * Math.PI;
	var offset:Number = Math.sqrt(Math.random()) * maxOffset;
	Dot._x = centerX + ( offset * Math.cos(angle) );
	Dot._y = centerY + ( offset * Math.sin(angle) );
}