我刚刚在ComputerGuru上遇到了一个关于Hacker News 的有趣问题,没有评论似乎给出了令人信服的答案.
为什么mt_rand(1, PHP_INT_MAX)总是返回奇数?
我不是原始问题的作者.
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)
PHP_INT_MAX这是2 63 -1(64位符号int max).
但是,mt_rand()不处理这么大的值.Mersenne twister在内部产生32位字,PHP mt_getrandmax()仅为2 31 -1(它抛出最高位).
要在您要求min的max范围内生成一个值,mt_rand首先得到0到2 31 -1的随机数,然后使用以下公式对其进行缩放:
x = ((x / (mt_getrandmax() + 1)) * (max - min + 1)) + min;
Run Code Online (Sandbox Code Playgroud)
(参见rand.c和php_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偶数时才这样.)