【问题标题】:How should I use random.jumpahead in Python我应该如何在 Python 中使用 random.jumpahead
【发布时间】:2010-03-30 14:35:54
【问题描述】:

我有一个应用程序执行某个实验 1000 次(多线程,以便同时完成多个实验)。每个实验都需要 appr。 50.000 个 random.random() 调用。

让这个真正随机的最佳方法是什么。我可以将一个随机对象复制到每个实验中,并且可以超过 50.000 * expid 的跳跃。文档表明 jumpahead(1) 已经扰乱了状态,但这真的是真的吗?

或者是否有另一种“最好的方式”来做到这一点?

(不,随机数不是用于安全性,而是用于都市哈希算法。唯一的要求是实验是独立的,而不是随机序列是否可预测)

【问题讨论】:

  • 另外,python 3 中最好的替代做法是什么(因为 .jumpahead 已被弃用)?

标签: python random


【解决方案1】:

我可以将一个随机对象复制到每个实验中,而不是提前 50.000 * expid。

大致正确。每个线程都有自己的Random 实例。

将它们全部播种到相同的种子值。使用常量进行测试,“运行记录”时使用/dev/random。

编辑。在 Python 之外和较旧的实现中,使用 jumpahead( 50000 * expid ) 来避免两个生成器以并行的值序列结束的情况。在任何合理的当前(2.3 后)Python 中,jumpahead 不再是线性的,使用expid 足以扰乱状态。

您不能简单地在每个线程中执行jumpahead(1),因为这将确保它们是同步的。使用jumpahead( expid ) 确保每个线程都被明显加扰。

文档表明 jumpahead(1) 已经扰乱了状态,但这是真的吗?

是的,jumpahead 确实“扰乱”了状态。回想一下,对于给定的种子,您会得到一个 - 长 - 但 固定 伪随机数序列。你在这个序列中领先。要通过随机性测试,您必须从这个 one 序列中获取所有值。

编辑。曾几何时, jumpahead(1) 是有限的。现在 jumpahead(1) 确实进行了更大的加扰。然而,加扰是确定性的。你不能简单地在每个线程中做jumpahead(1)

如果您有多个具有不同种子的生成器,则违反了“一个种子中的一个序列”假设,并且您的数字不会像从单个序列中获取它们一样随机。

如果您只跳头 1,您可能会得到可能相似的并行序列。 [这种相似性可能无法检测到; 理论上,有相似之处。]

当您超过 50,000 时,您确保遵循 1-sequence-1-seed 前提。您还可以确保在两次实验中不会出现相邻的数字序列。

最后,您还具有可重复性。对于给定的种子,您可以获得一致的结果。

同样的跳跃:不好。

>>> 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]

【讨论】:

    【解决方案2】:

    您不应该使用该功能。没有证据表明它可以在 Mersenne Twister 发电机上工作。确实是removed from Python 3 for that reason

    有关在并行环境中生成伪随机数的更多信息,请参阅this article from David Hill

    【讨论】:

      【解决方案3】:

      jumpahead(1) 确实足够了(在random 的当前实现中,与jumpahead(50000) 或任何其他此类调用相同——我相信它与基于 Mersenne Twister 的实现同时出现)。因此,请使用适合您程序逻辑的任何参数。 (当然,出于线程安全的目的,请为每个线程使用单独的 random.Random 实例,正如您的问题已经暗示的那样)。

      random 模块生成的数字并不意味着加密强度很高,所以最好不要将其用于安全目的;-)。

      【讨论】:

        【解决方案4】:

        根据 python.org 上的random module docs

        “您可以实例化您自己的 Random 实例以获取不共享状态的生成器。”

        正如您所提到的,还有一个关于 jumpahead 的相关注释。但是那里的保证有点模糊。如果对操作系统提供的随机性的调用不那么昂贵以至于支配你的运行时间,我会跳过所有的微妙之处并做一些类似的事情:

        randoms = [random.Random(os.urandom(4)) for _ in range(num_expts)]
        

        如果 num_expts 约为 1000,那么您的种子中不太可能发生任何碰撞(生日悖论表明您需要进行大约 65000 次实验才能有 >50% 的概率发生碰撞)。如果这对你来说还不够好,或者如果实验的数量更像是 100k 而不是 1k,那么我认为跟进这个是合理的

        for idx, r in enumerate(randoms):
          r.jumpahead(idx)
        

        请注意,我认为仅仅让你的种子更长(例如 os.urandom(8))是行不通的,因为随机文档声明种子必须是可散列的,等等 32 位平台上,您最多只能在种子中获得 32 位(4 字节)的有用熵。

        这个问题激起了我的好奇心,所以我去看了code实现随机模块。我绝对不是 PRNG 专家,但看起来 jumpahead(n) 中的 n 值略有不同会导致随机实例状态明显不同。 (总是害怕与 Alex Martelli 相矛盾,但代码确实在对随机状态进行洗牌时使用了 n 的值)。

        【讨论】:

        • 微妙的问题是独立种子不会让事情变得更加随机。他们可以让事情变得不那么随机。为了正确匹配底层数学,必须使用带跳转的单个种子。
        • 在 Python 2.3 之前的版本中,向前跳跃是序列中向前迈出的一个简单的线性步骤。这是 RNG 在许多其他库和语言中的共同特征。然而,Python 有一个不同的跳跃,这不是按顺序向前迈出的简单步骤。我会修正我的答案。
        • @S.Lott:真正独立的种子应该没有问题。伤害的是线性依赖。
        猜你喜欢
        • 1970-01-01
        • 2010-09-25
        • 2019-09-29
        • 1970-01-01
        • 2016-11-04
        • 2011-09-18
        • 2020-08-18
        • 2014-10-15
        • 2020-07-21
        相关资源
        最近更新 更多