-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
random
doesn't generate uniform numbers (very high correlation) when using seed from time
#8589
Comments
xoroshiro128+ (and most of pseudorandom number generator) generates random numbers by repeatedly updating internal state by calling Here is C code of xoroshiro128+ Following C code call #include <inttypes.h>
#include <stdio.h>
int main() {
int i;
for(i=0; i<20; ++i) {
//Today's unix timestamp
uint64_t x = i+1533876749;
s[0] = x >> 16;
s[1] = x & 0xffff;
next();
uint64_t n = next();
printf("%" PRIu64 "\n", n);
}
} output doesn't looks like random:
If you want to generate random number in such a way, use hash function or Counter-based random number generator. By the way, proc rand*(r: var Rand; max: range[0.0 .. high(float)]): float {.benign.} =
...
let u = (0x3FFu64 shl 52u64) or (x shr 12u64)
result = (cast[float](u) - 1.0) * max This code works only when float is IEEE 754 64bit float. According to Nim manual:
|
@timotheecour this is exactly not a bug, you seeding random number generator with ascending sequence of numbers, and this is not how PRNG (pseudo random number generators) must work. You need to seed it only once. |
Yeah, it is, I updated the spec. |
Yeah, no bug here. |
I know, I was simplifying. Issue remains when binary is run multiple times (each time it seeds once) I improved randomness by using nanoseconds instead of seconds as done in random.nim (which I just noticed now) proc randomize*() {.benign.} =
let now = times.getTime()
randomize(convert(Seconds, Nanoseconds, now.toUnix) + now.nanosecond) However I'd like to keep this issue open: D's version produces random looking results even when initialized from unix time in seconds: import std.stdio;
import std.random;
import std.datetime;
import std.conv;
void main(){
auto seed = Clock.currTime().toUnixTime;
auto seed2 = seed.to!int;
auto rnd = Random(seed2);
//auto x = uniform(0.0, 1.0, rnd);
auto x = uniform(0, int.max, rnd);
writefln("%10s %10s", seed2, x);
} rdmd main.d which shows it has better randomness than nim's version, so there's room for improvement, eg using https://en.wikipedia.org/wiki/Mersenne_Twister with a period of 2^^ 19937 |
https://cs.stackexchange.com/questions/50059/why-is-the-mersenne-twister-regarded-as-good "MT was regarded as good for some years, until it was found out to be pretty bad with the more advanced TestU01 BigCrush tests and better PRNGs. The table at pcg-random.org e.g. gives a good overview of features of some of the most used PRNGs, where the only "good" feature of the Mersenne Twister is the huge period, 2219937 and the possibility to use a seed (Reproducible Results), it passes the simple and fast TestU01 SmallCrush tests, but it fails some of the newer statistical quality tests, esp. TestU01's LinearComp Test and the TestU01's Crush and BigCrush Batteries." |
If you use random module at the same time in multiple threads/processes, you should seed them with same seed and call import times
import random
let t = now().toTime.toUnix
for i in 0..<20:
# Multiplier from
# https://en.wikipedia.org/wiki/Linear_congruential_generator#Parameters_in_common_use
let seed = int64(uint64(t+i) * 6364136223846793005u64)
# let seed = (t+i)
var r = initRand(seed)
var rf = r
echo r.next(), "\t", rf.rand(1.0) Applying good hash function to seed would also work but bit slower. You cannot say D's Random is better than nim xoroshiro128plus only from your code. MT(Mersenne Twister) is already in lib/pure/mersenne.nim If you still doubt of a quality of xoroshiro128+, xoshiro256** and xoshiro256+ are bigger version of it. |
thanks for the notes. regarding multithreading: |
fixed by #17468 import std/[times, random]
proc test()=
let seed = now().toTime.toUnix
seed.randomize
echo rand(1.0)
test() for i in {1..10}; do nim r --hints:off $timn_D/tests/nim/all/t12262.nim; sleep 1; done after PR:0.5795982367884893 before PR:0.3010194302173124 |
#[
nim c --nimcache:/tmp/nim//nimcache/ -o:/tmp/nim//app -r tests/nim/stdlib/t02_random_seed.nim
/tmp/nim//app
0.7110418528567215
/tmp/nim//app
0.7110418342302638
etc => BUG: they're all very close to each other
]#
The text was updated successfully, but these errors were encountered: