我该如何在Python中使用random.jumpahead

4 投票
4 回答
3049 浏览
提问于 2025-04-15 21:02

我有一个应用程序,它会进行某个实验1000次(是多线程的,所以可以同时进行多个实验)。每个实验大约需要调用50,000次random.random()。

那么,怎样才能让这些随机数真的随机呢?我可以把一个随机对象复制到每个实验中,然后让它跳过50,000 * expid的状态。文档上说jumpahead(1)已经可以打乱状态,但这是真的吗?

或者有没有其他更好的方法来做到这一点呢?

(不,这些随机数不是用来做安全方面的,而是用于一个名为metropolis hasting的算法。唯一的要求是实验之间要独立,而不是说随机序列是否可以预测之类的)

4 个回答

3

jumpahead(1) 这个函数其实是足够的(在当前的 random 实现中,它和 jumpahead(50000) 或其他类似的调用是一样的 -- 我觉得这个功能是在基于梅森旋转算法的实现同时出现的)。所以你可以根据你程序的逻辑来选择合适的参数。(当然,为了线程安全,最好每个线程使用一个独立的 random.Random 实例,正如你问题中提到的那样)。

random 模块生成的数字并不是为了满足加密安全的需求,所以你没有把它用于安全目的,这点很好;-)。

5

我可以把一个随机对象复制到每个实验中,然后跳过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]
3

你不应该使用那个函数。没有证据表明它可以在梅森旋转器(Mersenne Twister)生成器上正常工作。实际上,正因为这个原因,它在Python 3中被移除了

想了解更多关于在并行环境中生成伪随机数的信息,可以参考David Hill的这篇文章

撰写回答