为什么mt_rand(1,PHP_INT_MAX)总是返回一个奇数

Pie*_*ard 9 php random

我刚刚在ComputerGuru上遇到了一个关于Hacker News 的有趣问题,没有评论似乎给出了令人信服的答案.

为什么mt_rand(1, PHP_INT_MAX)总是返回奇数?

我不是原始问题的作者.

http://3v4l.org/dMbat

for ($i=0;$i<10000;$i++)
{
    echo mt_rand(1, PHP_INT_MAX)."\n";
}
Run Code Online (Sandbox Code Playgroud)

输出:

8571620074060775425
7401021871338029057
4351677773593444353
1801559362708176897
7848614552286527489
...
Run Code Online (Sandbox Code Playgroud)

Boa*_*ann 5

PHP_INT_MAX这是2 63 -1(64位符号int max).

但是,mt_rand()不处理这么大的值.Mersenne twister在内部产生32位字,PHP mt_getrandmax()仅为2 31 -1(它抛出最高位).

要在您要求minmax范围内生成一个值,mt_rand首先得到0到2 31 -1的随机数,然后使用以下公式对其进行缩放:

x = ((x / (mt_getrandmax() + 1)) * (max - min + 1)) + min;
Run Code Online (Sandbox Code Playgroud)

(参见rand.cphp_rand.h的源代码.)

基本上它盲目地缩放内部生成的数字以适应超大范围,甚至没有发出警告.乘以适合超大范围会在低位中生成大量零,然后添加min(即1)会使结果变为奇数.

这个问题在十六进制中更为显着,您可以看到每个数字的低32位完全是非随机的:

for ($i = 0; $i < 10000; $i++) {
    printf("%016x\n", mt_rand(1, PHP_INT_MAX));
}
Run Code Online (Sandbox Code Playgroud)

输出:

41e0449b00000001
53d33d7c00000001
6ec8855700000001
234140e000000001
13a4581900000001
77547beb00000001
35a0660a00000001
0d0cd44200000001
...
Run Code Online (Sandbox Code Playgroud)

手册中有一个注释试图警告这一点,虽然它低估了问题:

mt_rand()max超过2 32时,返回值的分布偏向于64位版本的PHP上的偶数.这是因为如果max大于返回的值mt_getrandmax(),则必须按比例放大随机数生成器的输出.

(它表示偏向偶数,但只有min偶数时才这样.)