Skip to content

Instantly share code, notes, and snippets.

@antirez
Last active March 13, 2017 19:11
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save antirez/52529aefb7000aa076f5fc6675252c3c to your computer and use it in GitHub Desktop.
Save antirez/52529aefb7000aa076f5fc6675252c3c to your computer and use it in GitHub Desktop.
/* MacOS libc PRNG is garbage!
* Test this on MacOS 10.12.3 (may be the same with other versions)
* You'll see three times the same output.
* Cheers by @antirez. */
#include <stdio.h>
#include <stdlib.h>
int main(void) {
/* Note, 1000000, 1000001, 1000002 are not special.
* You can use any other "near" seeds and MOD 7 the sequence will be the same. */
srand(1000000);
printf("%d\n", rand() % 7);
srand(1000001);
printf("%d\n", rand() % 7);
srand(1000002);
printf("%d\n", rand() % 7);
return 0;
}
@antirez
Copy link
Author

antirez commented Mar 13, 2017

Output on Linux:
3
0
5

Output on MacOS:
0
0
0

@mechanicker
Copy link

How about trying arc4random() on Mac

@dchest
Copy link

dchest commented Mar 13, 2017

@rahulkhadse
Copy link

rand() on mac has always frustrating for me. I would create another function using some algorithm which would give you desired result. Take a look at snippet using Poisson's Distribution to give random number within any range. This way its guaranteed to get different random number each time. Although that defies the definition of randomness, but that's what we need (distributed randomness) sometimes in programming.

int Generate_Random_within_Range (unsigned int min, unsigned int max) { int base_random = rand(); /* in [0, RAND_MAX] */ if (RAND_MAX == base_random) return Generate_Random_within_Range(min, max); /* now guaranteed to be in [0, RAND_MAX) */ int range = max - min, remainder = RAND_MAX % range, bucket = RAND_MAX / range; /* There are range buckets, plus one smaller interval within remainder of RAND_MAX */ if (base_random < RAND_MAX - remainder) { return min + base_random/bucket; } else { return Generate_Random_within_Range (min, max); } }

@dchest
Copy link

dchest commented Mar 13, 2017

Copying here from my twitter:

This isn’t a proper test for “weak” PRNG — you’re supposed to be seeding it once and then extracting random numbers. You just found a case where different initial seeds produce same outputs, but rand() doesn’t guarantee that it should produce different outputs in such case: it only guarantees that same srand() seed produces same sequence. If your seeds are pseudorandom — not related — rand() will likely produce what you expect (there’s sranddev(), which sets seed to a pseudorandom value). This is still not good, of course: rand(), random(), etc. are all old, bad PRNGs anyway on many systems.

BTW, OpenBSD replaced all those crappy libc PRNGs with one good cryptographically secure PRNG.

@colmmacc
Copy link

colmmacc commented Mar 13, 2017

Is this post satire? with an unbiased PRNG there will be a subsequence of three 0's like this in every 7^3 (343) sequence of seeds. 6 more if you're willing to accept a subsequence of any particular digit. Also this isn't an unbiased test. rand() % 7 will bias slightly in favor of the value 0 because RAND_MAX isn't a whole multiple of 7.

sigh. I missed the comment, and only read the code. It does look like rand() on Mac has some kind of internal periodicity. Which is bad.

@ChrisMcKee
Copy link

ChrisMcKee commented Mar 13, 2017

^ makes sense.

using the first seed gives

4
1
1
3
0

or

21585
18586
29373
4301
3304

without the modulus

every time.

Less a bug, more an intended, but sucky, feature.

@antirez
Copy link
Author

antirez commented Mar 13, 2017

There is some confusion here about crypto-PRNG, my example above, which is not limited to the seeds I selected, and so forth. So:

  1. The problem is not that the rand() sequence is deterministic. Here I'm not even checking that its distribution is random-looking.
  2. The special seeds I wrote above are just examples, the % 7 same results for different X, X+1, X+2 seeds will happen for almost all the seeds.
  3. It is trivial to have a fast PRNG that provides a decent distribution, and that by changing the seed produces a very different sequence.
  4. My claim is documented in the implementation itself... so the code acknowledges it.
  5. srand(time(NULL)) is a huge programming pattern to just say: give me a different sequence every different second I run it.

@antirez
Copy link
Author

antirez commented Mar 13, 2017

@colmmacc read my last post. You misunderstood the point completely. Happens for every possible N seeds that are just similar numbers.

@antirez
Copy link
Author

antirez commented Mar 13, 2017

TLDR: it happens even with this code.

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main(void) {
    int seed = time(NULL);
    srand(seed+1);
    printf("%d\n", rand() % 7);
    srand(seed+2);
    printf("%d\n", rand() % 7);
    srand(seed+10);
    printf("%d\n", rand() % 7);
    return 0;
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment