我正在玩perl的rand(),并注意到当提供大于2 ^ 32的参数时,输出的最后几位变得可预测。
我发现说明的最明确的方法是使用以下脚本:
srand(); for $i (1..10) { printf "%4x\n",rand(2**48)%2**16 }
每当我执行输出
时5101
6378
2a23
62f2
8d15
effc
9657
2d16
f669
40c0
(它不仅仅是前10个值,但我没有看到复制一长串"随机"数字的点)
对srand()的调用是多余的,但它可以很容易地提供一个参数,并且看到它没有改变任何东西。
我试过这个:
我知道rand()不应该是加密安全的,但是可预测的最后16位比我理解的更糟糕。我是否使用了任何错误的功能?
答案 0 :(得分:5)
这不仅仅是"默认"的实施。种子(srand
没有参数被召唤或根本没有被召唤),正如你可能从Schwern的回答中看到的那样,这个回答是在评论中联系起来的; 所有 srand
次调用(以及所有初始化RNG状态的方法)都会遇到此问题。
目前(自5.20开始)perl使用自己的基于FreeBSD drand48
和srand48
的RNG实现;在此之前,perl使用drand48
和srand48
(如果可用),因此Linux上的行为实际上是相同的。在任何一种情况下,srand
实现仅使用32位输入,将它们置于48位RNG状态的高32位;低16位初始化为0x330e
。如果你通过一轮LCG算法运行0x330e
(只需要前一个状态的低16位来获得下一个状态的最后16位),乘以0xe66d
并添加{{ 1}},你得到0x000b
,这是你观察到的第一个值。
显然,较低位的这种可预测性很差,而且我不确定为什么它没有得到解决,特别是现在perl有自己的RNG实现,很容易被48位清理。显然,它比高位始终初始化为已知状态的损害小。
目前,我建议如果您想对perl的RNG做任何严肃的事情,每次呼叫最多使用32位,必要时组合多个呼叫以获得更高的精度/范围。