【问题标题】:How to generate a repeatable random number sequence?如何生成可重复的随机数序列?
【发布时间】:2012-01-26 18:56:12
【问题描述】:

我想要一个可以生成伪随机值序列的函数,但该序列在每次运行时都是可重复的。我想要的数据必须在给定范围内合理地随机分布,它不一定是完美的。

我想编写一些代码,基于随机数据运行性能测试。我希望每台机器上的每次测试运行的数据都相同,但出于存储原因,我不想随测试一起发送随机数据(最终可能会达到数兆字节)。

random 模块的库似乎并没有说相同的种子在任何机器上总是会给出相同的序列。

编辑:如果您要建议我播种数据(如上所述),请提供说明该方法有效的文档,并且可以在一系列机器/实现上工作。

编辑:Mac OS X 上的 CPython 2.7.1 和 PyPy 1.7 以及 CPython 2.7.1 和 CPython 2.52=.2 Ubuntu 似乎给出了相同的结果。尽管如此,没有任何文档以黑白方式规定这一点。

有什么想法吗?

【问题讨论】:

  • 您是否尝试过多次生成具有给定种子的序列?
  • 我只有一台电脑和一个操作系统,所以我无法可靠地测试这个。
  • 我认为根本问题是“为了什么?”如果密码 - 这是非常糟糕的主意,不要这样做。你必须写“为了什么”。
  • @skippy:请阅读他的问题。他明确表示,他希望它们用于基于随机数据的性能测试,这是一件非常明智的事情。
  • 只是一个简短的评论,让其他人免于我的菜鸟错误:random.seed() 用于使随机可重复,但如果您的输入数据,您只会看到相同的结果也是一样的。这听起来很明显,但一定要检查一下。

标签: python random cpython


【解决方案1】:

为此,我使用了重复的 MD5 哈希,因为哈希函数的意图是跨平台的一对一转换,因此在不同平台上它总是相同的。

import md5

def repeatable_random(seed):
    hash = seed
    while True:
        hash = md5.md5(hash).digest()
        for c in hash:
            yield ord(c)

def test():
    for i, v in zip(range(100), repeatable_random("SEED_GOES_HERE")):
        print v

输出:

184 207 76 134 103 171 90 41 12 142 167 107 84 89 149 131 142 43 241 211 224 157 47 59 34 233 41 219 73 37 251 194 15 253 75 145 96 80 39 179 249 202 159 83 209 225 250 7 69 218 6 118 30 4 223 205 91 10 122 203 150 202 99 38 192 105 76 100 117 19 25 131 17 60 251 77 246 242 80 163 13 138 36 213 200 135 216 173 92 32 9 122 53 250 80 128 6 139 49 94

基本上,代码将获取您的种子(任何有效字符串)并重复对其进行哈希处理,从而生成从 0 到 255 的整数。

【讨论】:

  • 这是个好主意!
【解决方案2】:

存在平台差异,因此如果您在不同平台之间移动代码,我会选择 DrRobotNinja 描述的方法。

请看下面的例子。我的台式机上的 Python(带有 Core i7 的 64 位 Ubuntu,Python 2.7.3)为我提供了以下信息:

> import random
> r = random.Random()
> r.seed("test")
> r.randint(1,100)
18

但如果我在我的 Raspberry Pi(ARM11 上的 Raspbian)上运行相同的代码,我会得到不同的结果(对于相同版本的 Python)

> import random
> r = random.Random()
> r.seed("test")
> r.randint(1,100)
34

【讨论】:

  • 我不知道这是否是记录在案的行为。当 Python 标准库的大部分设计为跨平台工作时,这应该是平台相关的,这似乎很奇怪。也许我应该向 Python 团队提交错误?
  • 这是个好主意(在首先查看错误报告之后)。如果这不是错误,将会有一个很好的解释。
  • 我刚刚在两个 Ubuntu 副本上遇到了同样的问题。相同的 Linux 版本,相同的 Python 版本(2.7.3),相同的 GCC 版本。但是,一个是 32 位的,另一个是 64 位的。 64 位机器与您的 64 位版本 (18) 相同,而我的 32 位机器与您的 Pi (34) 相同。这必须是 32/64 位的东西。是否曾经创建过错误报告?
  • 不,很遗憾我从未提交过任何错误报告。对此感到抱歉。
  • 我写这个问题的时候没有加链接,所以这里是:stackoverflow.com/a/26592047/1065901基本上,问题是你没有用整数初始化生成器,但必须先对其他一些值进行哈希处理(这是平台相关部分)。
【解决方案3】:

如果随机数的质量不如跨平台的可重复性那么重要,您可以使用传统的linear congruential generators 之一:

class lcg(object):
    def __init__( self, seed=1 ):
        self.state = seed

    def random(self):
        self.state = (self.state * 1103515245 + 12345) & 0x7FFFFFFF
        return self.state

由于这是在您的程序中使用整数算术编码的,因此它应该可以在任何合理的平台上确定地重复。

【讨论】:

  • 太棒了,我去看看。
【解决方案4】:

为随机数生成器指定一个种子。如果您提供相同的种子,您的随机数也应该相同。

http://docs.python.org/library/random.html#random.seed

【讨论】:

  • 这就是我的想法,但正如我在问题中所说,我看不到任何支持这一点的文档。
  • @Oleksi - 仅在此机器上此操作系统上的此 Python 实现上。我的要求是它在不同的实现中表现相同(对于初学者来说,文档似乎建议随机种子是在 C 模块中生成的。PyPy 呢?)
  • @Joe 它没有被定义,因为这是种子的正式定义的一部分。没有伪随机算法可以用相同的种子给出不同的结果,这是不可能的。我想他们可能会提到它,但他们可能认为这对每个人来说都是显而易见的。
  • 据我所知,所有的伪随机数生成器都保持这个属性,相同的种子将生成相同的随机数集。它似乎是用于生成素数的底层算法的属性。
  • 我的猜想和其他人一样多,我来到这里是因为我无法用事实来支持我的假设。我们是否保证将始终使用相同的算法?
【解决方案5】:

还有一个答案,为什么来自this answer 的示例确实会在不同的机器上产生不同的输出:

这是因为在为随机生成器播种时,种子必须是整数。如果您使用一些非整数为生成器播种,则必须首先对其进行哈希处理。哈希函数本身不是独立于平台的(显然至少不是全部,如果你知道更多,请纠正我)。

所以把它们放在一起:Python 使用一个伪随机数生成器。因此,当从相同状态开始时,产生的随机数序列将始终相同,与平台无关。它只是一个确定性算法,没有来自外界的进一步输入。

这意味着:只要你用相同的状态初始化你的随机生成器,它就会产生相同的数字序列。可以使用相同的整数种子或通过保存并重新应用旧状态(random.getstate() 和 random.setstate())来达到相同的状态。

【讨论】:

  • 这似乎是一个真实的,如果部分答案,肯定会增加信息。如果我是你,我会删除顶部的道歉!
  • 它开始时要短得多,但发展成一个详尽的答案,所以你是对的,我删除/改写了它。
【解决方案6】:

文档没有明确说明提供种子将始终保证相同的结果,但 Python 基于所使用的算法的随机实现可以保证这一点。

根据文档,Python 使用Mersenne Twister 作为核心生成器。一旦这个算法被播种,它就不会得到任何会改变后续调用的外部输出,所以给它相同的种子,你得到相同的结果。

当然,您也可以通过设置种子并生成大量随机数列表并验证它们是否相同来观察这一点,但我知道不想单独相信这一点。

我没有检查过除了 CPython 之外的其他 Python 实现,但我非常怀疑它们是否会使用完全不同的算法来实现随机模块。

【讨论】:

  • 我就是这么想的。我可能最终会将此作为最糟糕的解决方案。
  • 即使他们使用完全不同的算法,如果你给相同的伪随机算法提供相同的种子,它会吐出相同的数字序列 - 如果你想测试你会遇到问题在使用不同算法的两个不同的python实现上,仅此而已。但据我了解,他们也保证了底层算法,所以没关系。
  • 在我看来,如果您使用 32 位 Mersenne Twister 与 64 位 Mersenne Twister,您仍然可能会得到不同的结果
【解决方案7】:

使用 random.seed(...) 可以生成可重复的序列。一个示范:

import random

random.seed(321)
list1 = [random.randint(1,10) for x in range(5)]

random.seed(321)
list2 = [random.randint(1,10) for x in range(5)]

assert(list1==list2)

之所以有效,是因为 random.seed(...) 不是真正随机的:它是伪随机的,在给定初始起始条件“种子”的情况下,通过置换某个状态机来产生连续的数字。

【讨论】:

  • 改用random.Random类,因为上面会改变模块级别seed,如果您的代码从其他地方调用randint,您可能会遇到麻烦。
【解决方案8】:

我刚刚尝试了以下方法:

import random
random.seed(1)
random.random()
random.random()
random.random()

random.seed(1)
random.random()
random.random()
random.random()

我在 CLI 中以不同的速度多次输入每一行。每次都产生相同的值。

【讨论】:

    【解决方案9】:

    一种选择是使用numpy.random,其目标是与平台无关,另请参阅cross platform numpy.random.seed()

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-10-23
      相关资源
      最近更新 更多