Python如何生成Mersenne twister随机数种子

5
如果没有提供显式的种子值,Python如何为内置的随机库中使用的Mersenne扭曲伪随机数生成器提供种子?它是否基于时钟?如果是这样,那么种子是在导入随机模块时找到的还是在第一次调用时找到的? Python的文档似乎没有答案。
4个回答

3

在现代Python版本中(参见http://svn.python.org/projects/python/branches/release32-maint/Lib/random.py),Random.seed尝试使用从/dev/urandom读取的32个字节。如果这样做不起作用,它将使用当前时间:(a是可选值,可用于显式地种子PRNG。)

    if a is None:
        try:
            a = int.from_bytes(_urandom(32), 'big')
        except NotImplementedError:
            import time
            a = int(time.time() * 256) # use fractional seconds

2
从这个答案中,我找到了random.py的源代码。在Random类中,对象构造时会设置种子。该模块实例化一个Random对象,并将其用于所有模块方法。因此,如果使用random.random()或另一个模块方法生成随机数,则种子是在导入时设置的。如果随机数由另一个Random实例生成,则种子是在构造该实例时设置的。
从源代码中可以看出:
# Create one instance, seeded from current time, and export its methods
# as module-level functions.  The functions share state across all uses
#(both in the user's code and in the Python libraries), but that's fine
# for most programs and is easier for the casual user than making them
# instantiate their own Random() instance.

2

种子基于时钟或(如果可用)操作系统源。当导入random模块时,它会创建(因此种子)一个共享的Random实例,而不是在首次使用时。

参考资料

Python文档中关于random.seed的说明:

random.seed(a=None, version=2)

Initialize the random number generator.

If a is omitted or None, the current system time is used. If randomness sources are provided by the operating system, they are used instead of the system time (see the os.urandom() function for details on availability).

random.py源代码(大幅删节):

from os import urandom as _urandom

class Random(_random.Random):

    def __init__(self, x=None):
        self.seed(x)

    def seed(self, a=None, version=2):
        if a is None:
            try:
                a = int.from_bytes(_urandom(32), 'big')
            except NotImplementedError:
                import time
                a = int(time.time() * 256) # use fractional seconds

# Create one instance, seeded from current time, and export its methods
# as module-level functions.  The functions share state across all uses
#(both in the user's code and in the Python libraries), but that's fine
# for most programs and is easier for the casual user than making them
# instantiate their own Random() instance.

_inst = Random()

最后一行处于顶层,因此在模块加载时执行。

按照维基百科文章的说法,如果使用种子0(作为单个uint32),则第1个状态变量应该为0(下一个应该为1),然而,从0开始播种并通过version = 1传递不使用python的随机数使第一个状态变量为0。你知道为什么吗?试试吧:online-python.com/Pxp4KycsC - DanielTuzes
1
@DanielTuzes 有两种初始化状态的方法:使用 init_genrand(整数种子,对应于维基百科中描述的魔术数字1812433253的初始化),或者使用init_by_array(数组作为种子,更复杂)。Python始终使用init_by_array,即使提供的种子是整数(除了在init_by_array中没有对init_genrand的引用之外),因此初始化不符合维基百科的描述。 - tom
我不知道random.py中的seed访问了什么,谢谢你的帮助!我怎样才能看到seed只调用了那个?(以便下次自己解决问题)不过很奇怪,因为numpy.random(uint32)使用了维基百科的初始化方法。 - DanielTuzes
1
我看到实现是在一个C模块中 - random.py中的Random类 继承自一个名为_random的模块中的Random类(根据约定,伴随C模块的通常有一个前导下划线)。我找到了C模块并向后工作:init函数在顶部有文档记录;我看到init_genrand与维基百科匹配(魔数1812433253有所帮助),并全局搜索“init_genrand”以查找调用者。 - tom
这比从 super().seed(a) 追踪到 将整数转换为数组 更容易。 - tom

0
其他答案都是正确的,但为了总结一下上面的评论,以防有人错过我今天追踪到的答案:

Mersenne Twister的典型参考实现接受种子,然后在内部(通常在构造函数中)调用this.init_genrand(seed)

如果你这样做并使用一个简单的数字,你将得到与Python使用不同的结果--也许会像我一样想知道为什么。

为了在另一种语言(我的情况下是node.js)中获得与Python相同的结果,您需要一个支持init_by_array方法的实现,然后用init_by_array([seed])初始化它。

如果您只是使用一个简单的32位int值,则此示例适用--如果您的种子是其他内容,则Python以不同的方式传递它(例如,大于32位的数字会分割并以32位每个数组元素发送等),但这应该至少可以帮助某些人朝正确的方向前进。

我最终使用的是https://gist.github.com/banksean/300494中的node.js实现,效果非常好。我在npm上找不到符合我需求的实现,可能需要自己添加一个。

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