我该如何在Python中使用random.jumpahead
我有一个应用程序,它会进行某个实验1000次(是多线程的,所以可以同时进行多个实验)。每个实验大约需要调用50,000次random.random()。
那么,怎样才能让这些随机数真的随机呢?我可以把一个随机对象复制到每个实验中,然后让它跳过50,000 * expid的状态。文档上说jumpahead(1)已经可以打乱状态,但这是真的吗?
或者有没有其他更好的方法来做到这一点呢?
(不,这些随机数不是用来做安全方面的,而是用于一个名为metropolis hasting的算法。唯一的要求是实验之间要独立,而不是说随机序列是否可以预测之类的)
4 个回答
jumpahead(1)
这个函数其实是足够的(在当前的 random
实现中,它和 jumpahead(50000)
或其他类似的调用是一样的 -- 我觉得这个功能是在基于梅森旋转算法的实现同时出现的)。所以你可以根据你程序的逻辑来选择合适的参数。(当然,为了线程安全,最好每个线程使用一个独立的 random.Random
实例,正如你问题中提到的那样)。
(random
模块生成的数字并不是为了满足加密安全的需求,所以你没有把它用于安全目的,这点很好;-)。
我可以把一个随机对象复制到每个实验中,然后跳过50,000 * expid的值。
大致上是对的。每个线程都有自己的Random
实例。
把它们的种子都设为相同的值。测试时用一个常量,正式运行时用/dev/random。
编辑。在Python之外和旧版本中,使用jumpahead(50000 * expid)
可以避免两个生成器产生相同的值序列。在任何较新的(2.3之后的)Python中,jumpahead
不再是线性的,使用expid
就足够打乱状态了。
你不能在每个线程中简单地使用jumpahead(1)
,因为这样会确保它们是同步的。应该使用jumpahead(expid)
来确保每个线程的状态都是独特的。
文档中提到jumpahead(1)已经可以打乱状态,这是真的吗?
是的,jumpahead确实会“打乱”状态。记住,对于给定的种子,你会得到一个很长但是固定的伪随机数序列。你是在这个序列中向前跳。为了通过随机性测试,你必须从这个唯一的序列中获取所有值。
编辑。曾几何时,jumpahead(1)的效果有限。现在,jumpahead(1)确实可以进行更大的打乱。不过,这种打乱是确定性的。你不能在每个线程中简单地使用jumpahead(1)
。
如果你有多个种子不同的生成器,就会违反“一个种子对应一个序列”的假设,你的数字就不会像从单一序列中获得的那样随机。
如果你只跳过1,你可能会得到相似的并行序列。[这种相似性可能无法被检测到;理论上,确实存在相似性。]
当你跳过50,000时,你确保遵循了“一个序列一个种子”的原则。你还确保在两个实验中不会有相邻的数字序列。
最后,你还可以保证结果的可重复性。对于给定的种子,你会得到一致的结果。
相同的jumpahead:不太好。
>>> y=random.Random( 1 )
>>> z=random.Random( 1 )
>>> y.jumpahead(1)
>>> z.jumpahead(1)
>>> [ y.random() for i in range(5) ]
[0.99510321786951772, 0.92436920169905545, 0.21932404923057958, 0.20867489035315723, 0.91525579001682567]
>>> [ z.random() for i in range(5) ]
[0.99510321786951772, 0.92436920169905545, 0.21932404923057958, 0.20867489035315723, 0.91525579001682567]
你不应该使用那个函数。没有证据表明它可以在梅森旋转器(Mersenne Twister)生成器上正常工作。实际上,正因为这个原因,它在Python 3中被移除了。
想了解更多关于在并行环境中生成伪随机数的信息,可以参考David Hill的这篇文章。