Normalized Random Numbers (Bell Curve)

Discuss programming in the QuakeC language.
Post Reply
Baker
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Normalized Random Numbers (Bell Curve)

Post by Baker »

I need normalized random numbers (you know, bell curve ... most of the results are typical with outliers being less common).

Now the best way I've determined to do this, is instead of using one random number and multiply by 3, is to take 3 random numbers and multiply by 1 and add them.

Kind of the board game role playing 3D6 approach:

Image

This was the best way I could think of with the limited math set in QuakeC, but for future use ... is there a "right" way to do this with an extension? The above method works ok for integers, but if you need random floats the above method isn't as useful.
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
Lardarse
Posts: 266
Joined: Sat Nov 05, 2005 1:58 pm
Location: Bristol, UK

Post by Lardarse »

Binomial approximations of normal distributions are ok upto a point.

Note that random() gives a float by default (you need to floor() or ceil() it, most usually the latter, to get an int).

Also note that the most obvious way of combining random numbers:

Code: Select all

damage = (random() + random() + random()) * 10;
may be susceptible to a compiler bug that will appear to result in all three calls to random() returning the same thing (the last one overwrites what the others actually returned), and that having each call to random() in its own statement (meaning separated by a semicolon) may be necessary to get the intended effect.

Random numbers that need to be properly normally distributed will be a lot harder. First of all, you'll need to be able to calculate this formula as needed: NormalDistrinution(x) = Image for -4<=x<=4. You'll need to define e as a constant (as is done for pi), and then use pow() and sqrt() (either from mathlib or from DP_QC_SINCOSSQRTPOW). Then something like:

Code: Select all

repeat {
x = crandom() * 4;
y = random();
} until (y < NormalDistribution(x));
return x;
and x will be normally distributed with a mean of 0 and variance of 1. Slow and time consuming. You may be able to take some shortcuts, but I don't know how useful they will be...

Also, that formula image prefers a white background...
Roaming status: Testing and documentation
Baker
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Post by Baker »

Lardarse wrote:Also, that formula image prefers a white background...
Sentient graphics ... good to know. I've always felt I wasn't keeping up with the latest developments and things like this prove I need to read the interwebs more.
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
Lardarse
Posts: 266
Joined: Sat Nov 05, 2005 1:58 pm
Location: Bristol, UK

Post by Lardarse »

Double posting for additional insight:

The Box-Muller transform (Wikipedia it) allows you to generate random normals by using two random floats (you will need to filter out random floats that == 1), but it requires:
# either sin() or cos()
# ln() of a number between 0 and 1
# sqrt() (note that this won't result in a complex number because ln(x) is negative if x < 1)
There is a slight variant method (Marsaglia polar method) that doesn't need sin(), but does require more filtering of random numbers.

If implementing an extension, all of those are available in C standard math libraries. However, I beleive that ln() isn't available to the QC yet (there is DP_QC_LOG, but I don't know if it's natural (base e) or base 10).

The method I suggest in the last post (essentially, throwing darts at the probability density function) was used to generate 100k normal deviations. I've not tried this, so I don't know what the rejection rate is, but I suspet it's pretty damn high, and will likely end up with a runaway loop error if implemented fully in QC.

There is one other thing that may provide interesting results: Using randomvec() (either by extension or mathlib), and then using just the x component. It won't go beyond -1 or +1 (so not a true normal deviate), but it will fall in a roughly bellcurve shape. It does however have the advantage of being very well supported...
Roaming status: Testing and documentation
Post Reply