如何查询 random.random() 使用的随机种子?

69

有没有办法找出Python用来生成随机数的种子?

我知道我可以指定自己的种子,但我很满意Python管理它。但是,我想知道它使用了什么种子,这样如果我喜欢在特定运行中得到的结果,我可以在以后再次重现该运行。如果我有当时使用的种子,那就好了。

如果答案是否定的,那么生成种子的最佳方法是什么?我希望它们在每次运行时都不同——我只想知道使用了什么种子。


math.random() 是什么?你是指 random.random() 吗? - ephemient
由于很难获取原始种子,所以我将从操作系统中生成一个自己的种子,例如 seed = int.from_bytes(os.urandom(8), byteorder="big") - Charlie Parker
7个回答

74

无法从生成器中恢复自动生成的种子。我通常会像这样生成种子:

seed = random.randrange(sys.maxsize)
rng = random.Random(seed)
print("Seed was:", seed)

这样它是基于时间的,因此每次您手动运行脚本时都会有所不同,但如果您使用多个生成器,则它们不会具有相同的种子,只是因为它们几乎同时创建。


4
默认情况下,PRNG会通过操作系统的PRNG(通过os.urandom)自动进行种子生成,因此几乎总是不需要手动设置种子。 - Glenn Maynard
33
除非你想知道种子是什么,以便稍后可以再现生成的序列。 - Zooba
4
Python缺少random.getseed()函数,但这比Java中的等效功能要容易得多。 - smci
11
在Python3+中,使用sys.maxsize作为已删除sys.maxint的替代。 - Brendan Maguire
2
@CharlieParker 在看到 Brendan 的评论后,我更新了我的答案,这就是为什么答案中写着 maxsize。之前它使用的是 maxint - Zooba
显示剩余3条评论

39

随机数生成器的状态并不总是简单的种子。例如,安全的伪随机数生成器通常具有熵缓冲区,它是一个较大的数据块。

然而,您可以保存和恢复整个随机数生成器的状态,以便以后可以再现其结果:

import random

old_state = random.getstate()
print random.random()

random.setstate(old_state)
print random.random()

# You can also restore the state into your own instance of the PRNG, to avoid
# thread-safety issues from using the default, global instance.
prng = random.Random()
prng.setstate(old_state)
print prng.random()

如果您想永久保存它,那么当然可以将getstate的结果序列化(pickled)。

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


这不会重现一个序列,它只会让你从上次离开的地方继续。如果你想从头开始重现整个序列,你需要知道种子值。 - Zooba
10
@Zooba:它们是等价的。要从馅饼中再现整个序列,只需在那一点上存储PRNG的状态即可。 - Glenn Maynard
9
考虑到问题的背景(可选的每次运行种子),存储一个相对较大的状态元组远非最佳选择。单个种子值更容易嵌入配置数据中,如果需要安全的伪随机数生成器,则无论如何都不应保存种子(或状态)。 - Zooba
2
这在技术上是正确的,但 Zooba 的方法对于 OP 的目的来说更加用户友好。 - smci
3
他的方法不必要地不安全,大幅度降低了可用的随机性。而认为您不应存储安全 PRNG 的状态的想法是荒谬的 - 这就好比说您不应该为 HTTPS 服务器存储私钥。 - Glenn Maynard
这非常有帮助。我的问题是,我(认为)需要种子来重现过去的模拟运行,因此创建新的种子并使用它不是一个选项。这个答案解决了我的问题。 - Davoud Taghawi-Nejad

22
您可以创建 random.Random 的子类,重写 seed() 方法并按照 Python(例如此示例中的 v3.5)的方式存储种子值在调用 super() 之前将其存储在变量中:
    import random

    class Random(random.Random):
        def seed(self, a=None, version=2):
            from os import urandom as _urandom
            from hashlib import sha512 as _sha512
            if a is None:
                try:
                    # Seed with enough bytes to span the 19937 bit
                    # state space for the Mersenne Twister
                    a = int.from_bytes(_urandom(2500), 'big')
                except NotImplementedError:
                    import time
                    a = int(time.time() * 256) # use fractional seconds

            if version == 2:
                if isinstance(a, (str, bytes, bytearray)):
                    if isinstance(a, str):
                        a = a.encode()
                    a += _sha512(a).digest()
                    a = int.from_bytes(a, 'big')

            self._current_seed = a
            super().seed(a)

        def get_seed(self):
            return self._current_seed
如果您进行测试,使用新种子生成的第一个随机值和使用相同种子(使用我们创建的get_seed()方法)生成的第二个值将相等:
    >>> rnd1 = Random()
    >>> seed = rnd1.get_seed()
    >>> v1 = rnd1.randint(1, 0x260)
    >>> rnd2 = Random(seed)
    >>> v2 = rnd2.randint(1, 0x260)
    >>> v1 == v2
    True
如果您存储/复制了庞大的种子值,并尝试在另一个会话中使用它,则生成的值将完全相同。

6
许多人并未欣赏这种方法。这个解释很棒。 - Doogle

7

由于没有人提到,通常在任何编程语言中获得的最佳随机样本是通过操作系统生成的,因此我必须提供以下代码:

random_data = os.urandom(8)
seed = int.from_bytes(random_data, byteorder="big")

这是加密安全的。

来源:https://www.quora.com/What-is-the-best-way-to-generate-random-seeds-in-python


对于我而言,当值为8时,它似乎会产生与sys.maxsize差不多数量的数字。

>>> int.from_bytes(os.urandom(8), byteorder="big")
17520563261454622261
>>> sys.maxsize
9223372036854775807
>>>

5
如果您使用random.seed(None)来"设置"种子,随机生成器会自动作为系统时间的一个函数进行播种。但是,正如您所观察到的,您无法访问此值。当我想要随机化但仍然知道种子时,我通常会这样做:
tim = datetime.datetime.now()
randseed = tim.hour*10000+tim.minute*100+tim.second
random.seed(randseed)

注意: 我之所以更喜欢这种方法而不是使用@Abdallah提出的time.time(),是因为这种方法生成的随机种子是人类可读的,并且可以立即理解,这通常有很大的好处。日期组件甚至微秒级别的组件也可以根据需要添加。


3

我也想做同样的事情,但我无法得到种子。所以,我想既然种子是从时间生成的,我就使用系统时间创建了我的种子,并将其用作种子,这样我就知道使用了哪个种子。

SEED = int(time.time())
random.seed(SEED)

2
种子是随机包中用于创建下一个随机数的内部变量。当请求新数字时,种子也会更新。
如果您想确保每次得到相同的随机数,我建议使用0作为种子,或者将其设置为可配置项。
CorelDraw曾经有一个随机图案生成器,它是通过种子进行初始化的。不同的种子会导致图案大不相同,因此种子是图案的重要配置信息。它应该是您运行的配置选项之一。
注:正如ephemient所指出的,随机数生成器的内部状态可能比种子更复杂,这取决于其实现方式。

2
种子实际上用于创建生成器的内部状态。通过调用“random()”可以达到许多状态,但不能直接由种子产生结果。因此,说种子是一个内部变量是不准确的——它只是为初始状态提供种子。 - ephemient
1
哦,我以为种子的长度会暗示可能的内部状态的长度。感谢您的纠正。 - Daniel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接