Хотя на первый взгляд может показаться, что macOS rand()
лучше не повторять никаких чисел, следует отметить, что при таком количестве сгенерированных чисел ожидается , чтобы увидеть множество дубликатов (в Фактически, около 790 миллионов или (2 31 -1) / е ). Аналогичным образом, повторение чисел в последовательности также не приведет к дублированию, но не будет считаться очень случайным. Таким образом, реализация Linux rand()
в этом тесте неотличима от истинного случайного источника, тогда как macOS rand()
- нет.
Еще одна вещь, которая на первый взгляд кажется удивительной, это Как MacOS rand()
может так хорошо избежать дублирования. Глядя на его исходный код , мы обнаруживаем, что реализация выглядит следующим образом:
/*
* Compute x = (7^5 * x) mod (2^31 - 1)
* without overflowing 31 bits:
* (2^31 - 1) = 127773 * (7^5) + 2836
* From "Random number generators: good ones are hard to find",
* Park and Miller, Communications of the ACM, vol. 31, no. 10,
* October 1988, p. 1195.
*/
long hi, lo, x;
/* Can't be initialized with 0, so use another value. */
if (*ctx == 0)
*ctx = 123459876;
hi = *ctx / 127773;
lo = *ctx % 127773;
x = 16807 * lo - 2836 * hi;
if (x < 0)
x += 0x7fffffff;
return ((*ctx = x) % ((unsigned long) RAND_MAX + 1));
Это действительно приводит ко всем числам от 1 до RAND_MAX
включительно, ровно один раз, до последовательность повторяется снова. Поскольку следующее состояние основано на умножении, состояние никогда не может быть нулевым (или все будущие состояния также будут равны нулю). Таким образом, повторное число, которое вы видите, является первым, а ноль - тем, которое никогда не возвращается.
Apple продвигает использование лучших генераторов случайных чисел в своей документации и примерах, по крайней мере, до тех пор, пока macOS (или OS X) существует, поэтому качество rand()
, вероятно, не считается важным, и они только что остановились на одном из простейших доступных генераторов псевдослучайных данных. (Как вы заметили, их rand()
даже комментируют с рекомендацией использовать вместо него arc4random()
.)
В связанной заметке самый простой генератор псевдослучайных чисел, который я мог найти, который дает приличные результаты в этом (и многие другие) тесты на случайность - это xorshift *:
uint64_t x = *ctx;
x ^= x >> 12;
x ^= x << 25;
x ^= x >> 27;
*ctx = x;
return (x * 0x2545F4914F6CDD1DUL) >> 33;
Эта реализация дает почти ровно 790 миллионов дубликатов в вашем тесте.