【问题标题】:Python: where is random.random() seeded?Python:random.random() 在哪里播种?
【发布时间】:2015-02-01 19:06:57
【问题描述】:

假设我有一些 python 代码:

import random
r=random.random()

一般来说,r 的值从哪里来?
如果我的操作系统没有随机数怎么办,那么它在哪里播种?
为什么不建议将其用于密码学?有什么方法可以知道随机数是什么?

【问题讨论】:

  • Here 你有类似的问题。

标签: python random cryptography random-seed


【解决方案1】:

按照代码

要查看random 模块在您系统中的“位置”,您可以在终端中执行:

>>> import random
>>> random.__file__
'/usr/lib/python2.7/random.pyc'

这为您提供了.pyc(“已编译”)文件的路径,该文件通常与可以找到可读代码的原始.py 并排放置。

让我们看看/usr/lib/python2.7/random.py发生了什么:

您会看到它创建了Random 类的实例,然后(在文件底部)将该实例的方法“提升”为模块函数。巧妙的把戏。当random 模块在任何地方导入时,会创建该Random 类的新实例,然后初始化其值并将方法重新分配为模块的函数,使其在每次导入时非常随机( erm... 或 per-python-interpreter-instance) 基础。

_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

cmets 已经告诉你发生了什么...这个方法尝试使用操作系统提供的默认随机生成器,如果没有,那么它将使用当前时间作为种子值。

但是,等等……那_urandom(16) 到底是什么东西?

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

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

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

假设我们在一个文明的操作系统中,例如 Linux(带有 real 随机数生成器)。 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、人类用户键入的击键、移动鼠标​​......)生成器是/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.urandom 或使用pycrypto 密码库中的随机数生成器。这些可能更随机。我说更随机因为...

图片灵感来自另一个SO answer

【讨论】:

  • 拥有真正的随机性并不总是更好。 OP 确实询问了密码学,但即使那样你也应该知道你使用数据的目的(例如,Salsa 总是从 0 产生 0)。播种伪随机数生成器对于可重复但任意的数据很有用,特别是对于随机生成的游戏中的可重复测试或共享关卡。
  • @YannVernier,正确,正确。这就是为什么我添加了一个说明,即有时使用相同的种子作为一项功能是可取的。
猜你喜欢
  • 2012-01-13
  • 1970-01-01
  • 2010-10-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-06-19
相关资源
最近更新 更多