Pseudo random number generators

VERY ROUGH WORK IN PROGRESS…NO REAL USEFUL INFORMATION AS OF YET.

A random number generator is like sex;
When it’s good, it’s wonderful,
And when it’s bad, it’s still pretty good.
George Marsaglia

Games? Crank 'em out as fast as you can; Wham! Bam! Thank you ma’am! Roquen

[h3]Overview[/h3]
The purpose of this page is to give an rough overview of how pseudo random number generators (PRNG) work and a rough idea of how to choose ones which are adequate for a game runtime.

[h3]Rules of thumb[/h3]

  • Never use the same instance of a generator in more than one thread.
  • Never create static utility methods which call a generator unless single threaded (same as previous).
  • If using a JDK supplied generator, use ThreadLocalRandom.

XXX Use different generators if you need a more expensive one for a sub-set of tasks…oh, and don’t really worry about any of this stuff if you just don’t generate many random numbers for a given task.

Everything below this point can (and should) be ignored unless you are generating a large numbers of random numbers per frame OR you tempted to use OR are using a “good” random number generator especially one with a period greater than 264. If a your most expensive AI entity needs up to half a million random numbers per frame, then a barely reasonable PRNG with a period of 232 should be more than sufficient.

Again this page is only dealing with PRNGs for game runtimes where “real” money is not directly involved…as in for gambling.

[h3]Introduction[/h3]
The vast majority of PRNGs all work in the same way. They take some state-data in the form of a sequence of bits and perform computations which mix the bits. Notice that hashing and random number generation are very similar. A given generator will have a period which is the number of unique values the state will assume before wrapping around and repeating itself. Yeah, that was clear. OK, let’s pretend like we’re using a 2-bit integer for our state data, so our 2-bit integer can have values of: {0,1,2,3}. For our random number generator we want to produce some permutation (MathWorld, Wikipedia) of that set which appears random. The number of permutations of a set with n elements is Factorial[n], so we have 24 choices for our 2-bit integer. Limiting the set to those that start with ‘0’ (all the remaining are rotations), we have: {0,1,2,3},{0,1,3,2},{0,2,1,3},{0,2,3,1},{0,3,1,2},{0,3,2,1}.

Now obviously none of the above permutation choices look very random but this is exactly how random number generators work. The problem here is that there just aren’t enough unique values that any of the permutations appear random. Luckily factorials explode and if we were talking about only 8-bits then there would be approximately 8.6x10506 choices and 16-bits is ~5.2x10287193. Other than some very narrow special cases (like in GLSL, when attempting to avoid using integers) there’s no reason to use generators with less than 32-bits.

An implication of this is that the period can be at most 2n, where ‘n’ is the number of bits. On this page we will only consider “full period generators” which will have a period of either 2n or 2n-1. The minus one comes into play for generators for which zero is an invalid state.

This does not imply that PRNGs with longer periods are superior in quality than those that are shorter.

[h3]Period[/h3]
As mentioned above the period of a PRNG is the length of the sequence that it generates. Once the period is exhausted, the sequence wraps-around and starts to repeat itself.

For scientific usage long periods are important. The reason for this is that statistically they will begin show defects if in “single computation” more than sqrt(T) random numbers are used, where T is the period. Note that the sqrt(T) = T<sup>1/2</sup>. Even more important is because one can break a problem across multiple CPUs/servers/whatever to attack a single problem and the longer the period, the lower the probability that the individual computation units will be using overlapping parts of the sequence.

Note the “single computation” part. There’s the mistaken notion in games that one needs a period long enough that it never repeats during gameplay. Not at all, it simply should be long enough that every single computation has more than enough values to complete.

So what about the period? Do we need to think in terms of T or sqrt(T)? The question is more or less moot. As noted above there’s no reason to use a generator with a period shorter than ~232, so you should feel safe if any single problem needs no more than 216 or 65536 random numbers, which seems highly unlikely. At the opposite end of the spectrum (SEE: url=http://www.java-gaming.org/topics/orders-of-magnitude/27917/view.html]orders of magnitude[/url]) it would take the Cray Titan more than 17 minutes to generate 264 random numbers and an infinite amount of time to generate 2128. Sticking the sqrt(T) rule of thumb this means you never need a generator with a period greater than 2128.

[h3]Dimensions[/h3]
Humm…any ideas here kids, other than don’t worry about dimensions. Just don’t use a LCG for more than 2.

[h3]Quality[/h3]
PRNGs do not attempt to create sequences which appear random. Instead they are attempting to simulate statistical randomness.

For our purposes the main usefulness of having a generator which passes the majority to all of XXXX
XX note uniform distribution
XX Write something somehow convincing about how quality a la. diehard and various crushes shouldn’t matter.

[h3]Old Skool[/h3]

Permutation Polynomials
SEE: GLSL Tricks

Weyl Generators
This family is based on the equidistribution theorem (properties of irrational numbers). Quality varies widely depending on formulation and precision. Like permutation polynomials they can be computed purely in floating point and are currently useful on the GPU.

The simplest version looks like:
u<sub>n</sub> = na mod 1 = (u<sub>n-1</sub>+a) mod 1

where:
‘a’ is a fixed irrational number mod 1.
‘n’ is the number in the sequence (0,1,2…)

Common variants include nested:
u<sub>n</sub> = n(na mod 1) mod 1

and shuffled nested (where ‘m’ is a fixed large integer):
v<sub>n</sub> = m(n(na mod 1) mod 1)+1/2
u<sub>n</sub> = (v<sub>n</sub>(v<sub>n</sub> a mod 1)) mod 1


  vec3 weyli(vec3 s, vec3 a)  { return fract(s+a); }            // simple incremental
  vec3 weyl(vec3 n, vec3 a)   { return fract(n*a); }            // simple explict
  vec3 weyln(vec3 n, vec3 a)  { return fract(n*fract(n*a)); }   // nested
  vec3 weylns(vec3 n, vec3 a)                                   // shuffled nested
  {
    n = m*fract(n*fract(n*a))+0.5;
    return fract(n*fract(n*a));
  }

On the CPU side these are of limted usefulness, mainly integer versions can be used in a combined generator (below). Integer versions use a scaled irrational number rounded to the nearest odd value. This results in an abysmal quality generator with a period of 2n where ‘n’ is the number of bits in the integer.


  /* 2<sup>31</sup>(3-sqrt(5)) = 0x61c88647 */
  private static final int WEYL = 0x61c88647;

  private int weyl;

  private void updateState() { weyl += WEYL; }

Linear Congruent Generators
Linear congruent generators are among the oldest and best understood family of generators. Their quality can vary from poor to state-of-the-art. It’s only worth considering the poorest quality and fastest variants: standard integers with power-of-two modulus. The first variant, the MLCG, is a single multiply and has a period of 2n-1 (zero is an illegal state). This is worth mentioning for the 4K contest.


  private int state;

  private static final int K = 741103597;

  public int nextInt() { state = K*state; return state; }

Of more general interest is the most common variant, which simply adds an odd constant at every step.


  private int state;

  public final int nextInt() {
    state = 0xac549d55 * state + 0x61c88647;    
    return state;
  }

LCGs have a very bad reputation. This is mainly due to some early C compilers using poorly chosen constants. Poorly chosen constants severly impact the quality of the generated sequence and this is true for all families of generators. A real issue with power-of-two LCGs is that the quality decreases based on bit position. The highest bit is the most random and each following bit is slightly less than the previous and the lowest bits aren’t random at all. This is less of a concern than one might imagine assuming your using a library with helper functions (nextInt(int n), nextFloat(), nextBoolean(), etc) that take this fact into account. In this case the user might only need to consider this fact if using raw state: ‘nextInt’ in the case of 32-bit and ‘nextLong’ in the case of 64-bit generators. As an example calling nextInt() and using the result as a set of boolean one should use the top bits.

SEE: “Tables of linear congruential generators of different sizes and good lattice structure”, Pierre L’Ecuyer.

[h3]New kids on the block[/h3]
A fair number of modernish generators are based on linear-shift-feedback register (LSFRs). Most popular generators which are considered state-of-the-art are based on (generalized) LSFR, such as Mersenne twister, WELL, et al. Generators like MT and WELL are insane overkill. For game related purposes interesting ones include: XorShift, XorWow and M-Sets.

SEE: “Fast random number generators based on linear recurrences modulo 2: Overview and Comparison”, Pierre L’Ecuyer and Francois Panneton

[h3]Combined generators[/h3]
When one family of generators has a statistical weakness at a given set of tests it can be combined with another family of generators that performs well at those tests.

[h3]Summary[/h3]

Reference
Blah, blah

How is this table for a replacement to the current table under the period section. Powers of numbers are incomprehensible beyond a certain point. The square root and cubed root doesn’t much sense. I don’t know what relation the root of the period has to the period. Mathematically you’re just showing n, 2^n, 2^(n/2), and 2^(n/3). Rather than show the decimal values, how about showing how long it would take for the prng to repeat itself? This chart shows the period of the rng (rows), how frequently a random number is pulled from the rng, and how long that time would take. I think it’s much more applicable to game programmers and is easier to comprehend in terms of scale.

[tr][td][/td][td]1 per second[/td][td]100/s[/td][td]10000/s[/td][td]1000000/s[/td][/tr]

[tr]
[td]2^8[/td]
[td]4m 16s [/td]
[td]2s [/td]
[td]0s[/td]
[td]0s[/td]
[/tr][tr]
[td]2^16[/td]
[td]18h 12m 16s [/td]
[td]10m 55s [/td]
[td]6s [/td]
[td]~0s[/td]
[/tr][tr]
[td]2^32[/td]
[td]1c 36y 70d 6h 28m 16s [/td]
[td]1y 132d 2h 27m 52s [/td]
[td]4d 23h 18m 16s [/td]
[td]1h 11m 34s [/td]
[/tr][tr]
[td]2^64[/td]
[td]5849424173c 55y 26d 7h 16s [/td]
[td]58494241c 73y 201d 18m 36s [/td]
[td]584942c 41y 268d 11h 2m 35s [/td]
[td]5849c 42y 152d 8h 1m 49s
[/td]
[/tr]

I can think of a scenario that might be a problem: The programmer chooses a random seed, the game runs for an amount of time and goes through a fraction of the rng’s period, the game runs again later with another random seed and halfway through hits a state it already had in the previous game. This probably isn’t a problem if user interaction can change the order/interpretation of bits taken from the rng, but maybe it’s a minor concern if multiplied over a large number of players in an online game.

Depending on how much real-world randomness (player interaction?) influences the game, the probability of the player experiencing deja vu changes. I doubt it’s a serious problem for most games though, maybe for a screensaver, but not a game. The butterfly effect could lead to different outcomes whether the player kills a familiar group of monsters in a different order or if he hits the space bar one millisecond later than he previously did, but maybe in a turn based rpg with a very small period rng someone might recognize a pattern and use it to game the AI.

This is exactly “counter” to what I’m attempting to say. Which is, you don’t (or rather shouldn’t) directly care about how long it takes before the RNG starts to repeating itself. If you’re running through the period multiple times in a single frame…that doesn’t directly matter. Indirectly it might mean you’re using an RNG with too short of a period…by which I mean “is too expensive per result”, as you shouldn’t be using anything with T shorter than 2^32 (again CPU only) of which there are fast good options. OR it means you’re generating tons of random numbers (sticking to 2^32) and generating more than 4x10^9 random numbers per game frame seems a bit excessive. On the other hand, if a generator isn’t going to repeat itself for a very long real-time, then that might again indicate that you’re throwing too much processor time on random number generation (assuming T > 2^32 or perhaps 2^64). Now this is completely counter-intuitive and many people will have trouble accepting this.

The purpose of the table with square and cube roots is (when I come up with some reasonable verbage) to give an idea of scale. For gaming purposes is insanely unlikely that the cube-root number is useful. I’m only including it for completeness AND as bound as people will tend to be somewhat paranoid about quality. Likewise, the square root number is mostly overkill but should be sufficient target for an acceptable programmer comfort zone. As mentioned above the sqrt & cube-root are rules of thumb from that statistics world about when a generator will start to show statistical defects IN A SINGLE PROBLEM. So, say one thing you need to do is generator a bunch of random vectors in 3D at some uniform grid positions and do something with those values. That’s an example of a single problem. Sticking to T=2^32, then sqrt(2^32) = 65536, divide by 3 (number of components/vector) = ~21845, cube root (for 3D) ~= 7281: So if the uniform grid of vectors is smaller than or equal to 7281x7281x7281…then you should be more than comfortable with the period. When the RNG moves on to the next problem, then you start anew. So all you really care about is the scale of the single largest problem.

Never mind. I missed a whole paragraph of text the first time I read it. You already covered everything I thought I was pointing out. I misread that section as just:

[quote]As mentioned above the period of a PRNG is the length of the sequence that it generates. Once the period is exhausted, the sequence wraps-around and starts to repeat itself.

There’s the mistaken notion in games that one needs a period long enough that it never repeats during gameplay. Not at all, it simply should be long enough that every single computation has more than enough values to complete. To give a rough idea of ‘scale’, here’s a table of common short periods with its square and cube roots:
[/quote]
I wasn’t suggesting that exhausting the entire period of a prng was a practical concern. Just that it was a better example of scales. Ignore it, since it doesn’t cover the same things. Maybe a another possibility is to have a chart like this and just make a note that the square root of 2^n is just 2^(n/2) and 2^(n/3) for cube roots.

[tr][td] [/td][td]Decimal Value[/td] [td]Note[/td][/tr]
[tr][td]2^16[/td] [td]65536[/td] [td]Greater than the number of ____[/td][/tr]
[tr][td]2^32[/td] [td]4294967296[/td] [td]Greater than the number of ____ in the ____[/td][/tr]
[tr][td]2^n[/td] [td]123456789…012345678[/td] [td]Some note about the scale of this number[/td][/tr]

I agree with you on everything on the period of a PRNG now, but the presentation on this page could be improved. First, I don’t see how roots give a better concept of scale because of the relation between roots and powers. It’s like a bad dictionary defining a word in terms of another form of that same word. Second, you need to explain what square roots and cube roots represent and how they should be analyzed. I’m vaguely familiar with the ideas. Enough so that I’m confident that you know what you’re talking about, but not familiar enough that I could explain it to another person. Explain what you mean by birthday spacing, what the roots mean from a statistical perspective, and when you would need to be concerned about either T^1, T^(1/2), or T^(1/3) in game programming.

Nice feedback…thanks. I work on adding the stuff you’ve mentioned.

Minor updates. Ripped out some useless distracting junk. Gone is the table and the mention of cube-root. Cube root is gone because that rule of thumb seems to only apply to LCGs and lower quality generators and there’s about zero chance a game runtime problem being related to birthday spacing.

Why the square root of the period?

My knowledge of number theory, probability and statistic is not sound enough to give an accurate nor convincing response. My understanding of the reasoning is as follows. http://en.wikipedia.org/wiki/Ramsey_theory coupled with:
http://en.wikipedia.org/wiki/Pigeonhole_principle
http://en.wikipedia.org/wiki/Birthday_problem

And I believe it’s exactly the same notions as why the http://en.wikipedia.org/wiki/Birthday_attack works.

PRNGs are sequences which generated by some method based on the properties of the chosen algebra or algebras. Examples LCGs are based on the integer mod some-smaller integer. LSFR are based on 2-adic numbers (or bits). An XorWOW combines 2-adic with integers. And (via Ramsey’s) once enough ‘data’ has been seen, then the underlying structure starts to become obvious.

From here: http://en.wikipedia.org/wiki/Birthday_problem#Approximations, we have:

Pr[(n,m)] ~= 1-exp(-m2/2n)

Setting the probability = .5 (50%) and solving for

1-exp(-m2/2n) = 1/2
exp(-m2/2n) = 1/2
-m2/2n = log(1/2)
m2 = -2n log(1/2)
m = sqrt(2*log(2)) sqrt(n)
m ~= 1.17741 sqrt(n)

Tossing away the scale factor (1.17741) gives an additional 17% safety margin…and thus was born the rule-of-thumb (I think).

Or I’m making this harder than it really is and the root is from:
http://en.wikipedia.org/wiki/Law_of_large_numbers and
http://en.wikipedia.org/wiki/Central_limit_theorem.

As far as I’m concerned, folks like George Marsaglia, Pierre L’Ecuyer & Richard Brent have all mentioned this rule of thumb and that’s good enough for me.

Hot off the presses: To any RNG nut cases, I’m so happy I could cry. A new random number generator almost as fast as a XorShift that (the paper claims) beats WELL1024a or MT19937 (that’s the Mersenne Twister) at Crush and BigCrush. To others…this is FASTER than ThreadLocalRandom and many times faster than Random…both of which are aweful.

SEE: http://xorshift.di.unimi.it.

I’ll toss a version into the grabbag later.

Hot off the presses: To any RNG nut cases, I’m so happy I could cry. A new random number generator almost as fast as a XorShift that (the paper claims) beats WELL1024a or MT19937 (that’s the Mersenne Twister) at Crush and BigCrush. To others…this is FASTER than ThreadLocalRandom and many times faster than Random…both of which are aweful.

SEE: http://xorshift.di.unimi.it.

I’ll toss a version into the grabbag later.

itaMNuWLzJo

itaMNuWLzJo

Using a few boxes I tossed a couple days worth of CPU time at (half a dozen Crush and tons of SmallCrush) tests and haven’t found a fail. I’ll let the experts tear this apart for scientific usage. Bottom like is this is very fast and performs better at statistical tests than some slower (including much slower) methods do. On my box it’s faster than ThreadLocalRandom, so this is an excellent choice for general game purposes (again excluding gambling.)

For those that care (hopefully you don’t) all Crush and SmallCrush tests in the batteries were started with a low hamming weight seed values in an attempt to cause failures.

Using a few boxes I tossed a couple days worth of CPU time at (half a dozen Crush and tons of SmallCrush) tests and haven’t found a fail. I’ll let the experts tear this apart for scientific usage. Bottom like is this is very fast and performs better at statistical tests than some slower (including much slower) methods do. On my box it’s faster than ThreadLocalRandom, so this is an excellent choice for general game purposes (again excluding gambling.)

For those that care (hopefully you don’t) all Crush and SmallCrush tests in the batteries were started with a low hamming weight seed values in an attempt to cause failures.

I found by chance this discussion… my 2€¢:

  1. The data reported at http://prng.di.unimi.it/ is entirely public and open. You can redo the computation from scratch, or inspect the TestU01 results.
  2. It is not very useful to start a xorshift64* generator with low hamming weights because due to the small state space it gets back to normality after very few iterations; the effect would be more pronounced on, say, the Mersenne Twister, which has a horrible escape-from-zeroland time.
  3. The whole xorshift* business was started with the idea of replacing ThreadLocalRandom—the biggest ever made mistake by the Java developers. They could have implemented any reasonable new generator with a small state space, and instead they replicated Random and its horrible congruential linear generator: if you want to get scared, https://twitter.com/joshbloch/status/269478731238760448
  4. I’ll publish soon a slightly different generator with 128 bits of space that beats ThreadLocalRandom on every type of call except nextInt(); you might be interested in that, too.

Ciao!

Wow, I think this is the first time the author of something popped-out-of-nowhere to comment on a comment of mine…if you follow me. I very jazzed by your RNG because it’s damn cheap and I can’t find any statistical holes for sampling sizes of interest here.

I always like to attempt to poke holes in thing myself and choosing low hamming weighs seemed liked like the most likely to produce failures.

Uggh. Additionally the non-randomness is coming from the seeding process since the generators are never moving through the sequence.

I’m curious to look at it, but from a practical standpoint a 2^64-1 period is way larger than a game runtime needs and the sampling sizes are small enough that even if some statistical randomness test were important the 64-bit version should be more than good enough. That’s why I was running SmallCrush tests rather than Crush. Well unless somehow a java version of the 128-bit version was faster than the 64-bit. Is the 128 bits of state stored as two 64-bit data data chunks?

P.S. If you’re ever bored you could peek at the wiki-ish page at the top and give any comments that come to mind. :wink:

I found by chance this discussion… my 2€¢:

  1. The data reported at http://prng.di.unimi.it/ is entirely public and open. You can redo the computation from scratch, or inspect the TestU01 results.
  2. It is not very useful to start a xorshift64* generator with low hamming weights because due to the small state space it gets back to normality after very few iterations; the effect would be more pronounced on, say, the Mersenne Twister, which has a horrible escape-from-zeroland time.
  3. The whole xorshift* business was started with the idea of replacing ThreadLocalRandom—the biggest ever made mistake by the Java developers. They could have implemented any reasonable new generator with a small state space, and instead they replicated Random and its horrible congruential linear generator: if you want to get scared, https://twitter.com/joshbloch/status/269478731238760448
  4. I’ll publish soon a slightly different generator with 128 bits of space that beats ThreadLocalRandom on every type of call except nextInt(); you might be interested in that, too.

Ciao!

Wow, I think this is the first time the author of something popped-out-of-nowhere to comment on a comment of mine…if you follow me. I very jazzed by your RNG because it’s damn cheap and I can’t find any statistical holes for sampling sizes of interest here.

I always like to attempt to poke holes in thing myself and choosing low hamming weighs seemed liked like the most likely to produce failures.

Uggh. Additionally the non-randomness is coming from the seeding process since the generators are never moving through the sequence.

I’m curious to look at it, but from a practical standpoint a 2^64-1 period is way larger than a game runtime needs and the sampling sizes are small enough that even if some statistical randomness test were important the 64-bit version should be more than good enough. That’s why I was running SmallCrush tests rather than Crush. Well unless somehow a java version of the 128-bit version was faster than the 64-bit. Is the 128 bits of state stored as two 64-bit data data chunks?

P.S. If you’re ever bored you could peek at the wiki-ish page at the top and give any comments that come to mind. :wink:

I like this Sebastiano Vigna person. New paper, shorter dependency chain generator and higher quality. What’s not to like?

https://github.com/roquendm/JGO-Grabbag/blob/master/src/roquen/math/rng/XorPlus128.java


    long s0 = data0;
    
    data0  = s0;

???

I like this Sebastiano Vigna person. New paper, shorter dependency chain generator and higher quality. What’s not to like?

https://github.com/roquendm/JGO-Grabbag/blob/master/src/roquen/math/rng/XorPlus128.java


    long s0 = data0;
    
    data0  = s0;

???