Python: random.random()的种子在哪里?

8

假设我有一些 Python 代码:

import random
r=random.random()

在通常情况下,r的值从哪里得到?
如果我的操作系统没有随机数,它将从哪里种子?
为什么不建议在加密中使用此种方法?有办法知道随机数是多少吗?


你可以在这里找到类似的问题:https://dev59.com/cWkv5IYBdhLWcg3wzj41 - Marcin
1个回答

19
请跟随代码。
要查看在你的系统中“random”模块的位置,你可以在终端中执行以下命令:
>>> import random
>>> random.__file__
'/usr/lib/python2.7/random.pyc'

那将为您提供到“编译”文件.pyc的路径,通常与可读代码的原始.py文件并排放置。
让我们看看在/usr/lib/python2.7/random.py中发生了什么:
您会注意到它创建了一个Random类的实例,然后(在文件底部)将该实例的方法“提升”为模块函数。很棒的技巧。当随机模块在任何地方被导入时,都会创建该Random类的新实例,然后初始化其值并将方法重新分配为模块的函数,使其在每次导入(嗯...或每个Python解释器实例)时变得相当随机。
_inst = Random()
seed = _inst.seed
random = _inst.random
uniform = _inst.uniform
triangular = _inst.triangular
randint = _inst.randint

这个Random类在其__init__方法中唯一做的事情就是对其进行种子处理:
class Random(_random.Random):
    ...
    def __init__(self, x=None):
        self.seed(x)    
...
_inst = Random()
seed = _inst.seed

所以... 如果 xNone(没有指定种子),会发生什么?好吧,让我们来检查一下 self.seed 方法:
def seed(self, a=None):
    """Initialize internal state from hashable object.

    None or no argument seeds from current time or from an operating
    system specific randomness source if available.

    If a is not None or an int or long, hash(a) is used instead.
    """

    if a is None:
        try:
            a = long(_hexlify(_urandom(16)), 16)
        except NotImplementedError:
            import time
            a = long(time.time() * 256) # use fractional seconds

    super(Random, self).seed(a)
    self.gauss_next = None

评论已经说明了正在发生的事情... 这种方法尝试使用操作系统提供的默认随机生成器,如果没有,则将当前时间用作种子值。

但是,等等...那个_urandom(16)是什么东西?

嗯,答案在这个random.py文件的开头:

from os import urandom as _urandom
from binascii import hexlify as _hexlify

塔达......种子是一个16字节的数字,来自os.urandom

假设我们在一个文明的操作系统中,比如Linux(有一个真正的随机数生成器)。 random模块使用的种子与以下操作相同:

>>> long(binascii.hexlify(os.urandom(16)), 16)
46313715670266209791161509840588935391L

指定种子值被认为不是很好的原因是因为random函数并不真正“随机”... 它们只是一系列非常奇怪的数字。但是,给定相同的种子,该序列将是相同的。您可以自己尝试:
>>> import random
>>> random.seed(1)
>>> random.randint(0,100)
13
>>> random.randint(0,100)
85
>>> random.randint(0,100)
77

无论何时、如何或甚至在哪里运行该代码(只要用于生成随机数的算法保持不变),如果您的种子是1,则始终会得到整数13、85、77...这有些违背了目的(有关伪随机数生成,请参见this)。另一方面,有use cases情况下,这实际上可能是一个理想的特性。
因此,被认为“更好”的是依赖于操作系统的随机数生成器。这些通常基于硬件中断计算,这些中断非常随机(它包括硬盘读取的interruptions、人类用户键入的按键、移动鼠标等...)。在Linux中,该操作系统生成器是/dev/random。或者,稍微挑剔一点,/dev/urandom(这就是Python的os.urandom实际上内部使用的内容)。区别在于(如前所述),/dev/random使用硬件中断来生成随机序列。如果没有中断,/dev/random可能会用尽,您可能需要等待一小段时间才能获得下一个随机数。/dev/urandom在内部使用/dev/random,但它保证始终为您准备好随机数字。
如果你使用的是Linux系统,只需要在终端输入cat /dev/random命令(准备好按下Ctrl+C因为它会输出非常随机的内容)。
borrajax@borrajax:/tmp$ cat /dev/random
_+�_�?zta����K�����q�ߤk��/���qSlV��{�Gzk`���#p$�*C�F"�B9��o~,�QH���ɭ�f�޺�̬po�2o�(=��t�0�p|m�e
���-�5�߁ٵ�ED�l�Qt�/��,uD�w&m���ѩ/��;��5Ce�+�M����
~ �4D��XN��?ס�d��$7Ā�kte▒s��ȿ7_���-     �d|����cY-�j>�
                    �b}#�W<դ���8���{�1»
.       75���c4$3z���/̾�(�(���`���k�fC_^C

Python使用操作系统的随机生成器或时间作为种子。这意味着我能想象到Python的random模块可能存在潜在弱点的唯一场景是:

  • 在没有实际随机数生成器的操作系统中,
  • 在设备上,time.time始终报告相同的时间(基本上是有损坏的时钟)

如果您关心random模块的实际随机性,可以直接使用os.urandompycrypto加密库中的随机数生成器。那些可能更加随机。我说“更加随机”是因为...

https://stackoverflow.com/a/2146062/289011

图片灵感来自于这个SO答案


拥有真正的随机性并不总是更好。虽然OP确实在问密码学方面的问题,但即使在这种情况下,您也应该了解您使用数据的目的(例如,Salsa始终会从0产生0)。对于可重复但任意的数据,特别是用于可重复测试或共享随机生成游戏中的级别,种子伪随机数生成器非常有用。 - Yann Vernier
@YannVernier,没错,没错。这就是为什么我添加了一个澄清说明,有时使用相同的种子作为一种特性是可取的。 - Savir

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