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

时间:2015-07-24 13:40:03

标签: php random

我刚刚从HackG News上的ComputerGuru遇到interesting question,似乎没有任何评论可以给出令人信服的答案。

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

我不是原问题的作者。

http://3v4l.org/dMbat

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

输出:

8571620074060775425
7401021871338029057
4351677773593444353
1801559362708176897
7848614552286527489
...

1 个答案:

答案 0 :(得分: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;

(请参阅rand.cphp_rand.h的来源。)

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

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

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

输出:

41e0449b00000001
53d33d7c00000001
6ec8855700000001
234140e000000001
13a4581900000001
77547beb00000001
35a0660a00000001
0d0cd44200000001
...

有一条注释in the manual试图警告这一点,虽然它低估了这个问题:

  

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

(它说它偏向偶数,但只有当min是偶数时才会这样。)