uint64_t lemur64(void) {
static uint128_t s = 2131259787901769494;
return (s *= 15750249268501108917ull) >> 64;
}
The test suite on that page says it's fine. master jart@nightmare:~/cosmo2$ o/examples/getrandom.com -b lemur64 | RNG_test stdin64 -seed 2131259787901769494
RNG_test using PractRand version 0.95
RNG = RNG_stdin64, seed = 2131259787901769494
test set = core, folding = standard (64 bit)
rng=RNG_stdin64, seed=2131259787901769494
length= 512 megabytes (2^29 bytes), time= 3.2 seconds
no anomalies in 229 test result(s)
rng=RNG_stdin64, seed=2131259787901769494
length= 1 gigabyte (2^30 bytes), time= 7.5 seconds
no anomalies in 246 test result(s)
rng=RNG_stdin64, seed=2131259787901769494
length= 2 gigabytes (2^31 bytes), time= 15.0 seconds
no anomalies in 263 test result(s)
rng=RNG_stdin64, seed=2131259787901769494
length= 4 gigabytes (2^32 bytes), time= 28.6 seconds
no anomalies in 279 test result(s)
rng=RNG_stdin64, seed=2131259787901769494
length= 8 gigabytes (2^33 bytes), time= 55.7 seconds
no anomalies in 295 test result(s)
rng=RNG_stdin64, seed=2131259787901769494
length= 16 gigabytes (2^34 bytes), time= 108 seconds
no anomalies in 311 test result(s)
There's also Scott Adams' prng: uint64_t dilbert_rand(void) {
return 9;
}
Another one of my favorites is UNIXv6: int rand(void) {
static int gorp;
gorp = (gorp + 625) & 077777;
return gorp;
}While multiplicative congruential PRNGs with a 32-bit state are poor, those with an 128-bit state, like yours, may be quite good.
However, for any application that needs pseudo-random numbers, you must have a good understanding of its requirements, in order to be able to choose the right PRNG.
For example, when the random numbers are used to choose the coordinates of random points in a multi-dimensional space, simple congruential PRNGs are guaranteed to show some correlations between the coordinates that should have been independent, though for a PRNG with an 128-bit state, unless it uses a poorly chosen multiplicative constant, the correlations will appear only for rather long sequences.
There is another kind of PRNG, which has exactly the same speed as yours, but which produces much better pseudo random sequences, the MLFG (Multiplicative Lagged Fibonacci Generator, 1984). Unlike yours, it is a non-linear generator, which is the reason for the good quality. While the speed is the same, it uses a larger state.
The choice of a PRNG can be important in 2 cases, for embedded microcontrollers, like in the parent article, and for applications which have to generate a huge amount of random numbers, e.g. billions of numbers per second.
Otherwise, there is a simpler choice, just use AES in counter mode as the PRNG.
Even when cryptographic strength is not needed, it does not hurt. The AES speed on all modern CPUs is not much lower than that of the fastest PRNGs, and only very few applications need faster PRNGs than AES. Moreover, a PRNG in counter mode has many advantages over the PRNGs based on a recurrence formula. It is possible to access quickly any arbitrary point in the random sequence. It is possible to split the random sequence in any number of subsequences, which can be computed in parallel and which are guaranteed to be uncorrelated, which is important in many multi-threaded programs.
btw I never needed a malloc for my baremetal firmwares so far. 10KB just for a stack and heap? crazy, that's way too much. recursion is forbidden anyway, and sbrk not provided. some unfortunate libc's need that, but MISRA will not be happy.