【问题标题】:Is there a predictable replacement for os.urandom using Python's random module?os.urandom 使用 Python 的 random 模块是否有可预测的替代品?
【发布时间】:2016-09-18 06:55:32
【问题描述】:

我希望得到 n 个随机字节作为一个或多个字节数组,就像 os.urandom 所做的那样。

但是我需要能够设置一个随机种子,以便该值是可重现的。

def urandom_from_random(rng, length):
    return bytes([rng.randint(0, 255) for i in range(length)])

import random
rng = random.Random(42)
data = urandom_from_random(rng, 120)
print(data)

上面的脚本有效,但效率不高。

除了创建许多整数并将它们转换为字节之外,还有更直接的方法吗?


注意:

  • 让 urandom 返回可预测的结果是 possible on Linux,但需要 root 访问权限。

【问题讨论】:

    标签: python-3.x random


    【解决方案1】:

    我认为random 中最接近您想要的功能是getrandbits。它返回一个具有请求位数的整数。如果你想把它变成一个bytes 实例,你可以使用int.to_bytes

    这是一个将这两者配对的快速函数:

    def urandom_from_random(rng, length):
        if length == 0:
            return b''
        integer = rng.getrandbits(length * 8)
        result = integer.to_bytes(length, sys.byteorder)
        return result
    

    您指定给to_bytes 的字节顺序无关紧要。我告诉它使用系统的本机字节顺序,但我实际上不知道这是否会比其他方式更快。

    【讨论】:

    • random.getrandbits() 在内部使用“little endian”来组合由 Mersenne Twister 算法 (MT19937) 生成的 32 位字。不知道对什么有影响
    • 这受到 int 的最大大小的限制,如果值很大,则会引发异常 OverflowError: signed integer is greater than maximum
    • 您需要请求多少字节才能获得该异常? Python 的 int 类型的大小是可变的,因此无论涉及的大小如何,它都应该可以正常工作。我测试了高达一万的length 值,没有任何问题。您是在运行我的实际代码还是一些变体?你确定你没有引入任何错误吗?你提到的例外是int.to_bytes 如果你给它一个太短的字节长度而不是你调用它的int 值会引发什么。
    • 在这种情况下,问题在于 getrandbits 的 C 代码如何处理 size 参数。它将位数解析为普通的 C int,在我的 64 位系统上仍然只有 32 位。我刚刚测试了一个实现补丁,使它使用ssigned_t 类型,并且确实允许更大的值。实际上,它允许值比我实际处理的内存大得多(因此对于某些值,我得到MemoryErrors 而不是OverflowErrors)。
    • 我已将此作为(次要)错误报告给 Python 核心开发人员:bugs.python.org/issue27072 因为它可能仅适用于 Windows 64 位版本(其中 intsize_t 有不同大小),并且仅在请求非常愚蠢的随机数据时,我不确定它的优先级有多高。
    【解决方案2】:

    这是@Blckknght 答案的修改版本,它考虑了getrandbits 中的内部INT_MAX 限制。

    def urandom_from_random(rng, length):
        if length == 0:
            return b''
    
        import sys
        chunk_size = 65535
        chunks = []
        while length >= chunk_size:
            chunks.append(rng.getrandbits(
                    chunk_size * 8).to_bytes(chunk_size, sys.byteorder))
            length -= chunk_size
        if length:
            chunks.append(rng.getrandbits(
                    length * 8).to_bytes(length, sys.byteorder))
        result = b''.join(chunks)
        return result
    

    使用示例:

    import random 
    rng = random.Random(42)
    print(len(urandom_from_random(rng, 300000000)))
    

    【讨论】:

      【解决方案3】:

      在代码开头使用种子:

      import random
      random.seed(7)
      rng = random.Random(42)
      data = urandom_from_random(rng, 120)
      

      只要您在种子中使用 7,您就有相同的随机选择。然后您可以将其更改为任何值。

      【讨论】:

      • random.seed(7) 不是必需的。该脚本可以按原样运行,澄清了我的问题。
      猜你喜欢
      • 2011-07-17
      • 2020-06-11
      • 2014-12-14
      • 1970-01-01
      • 2012-10-14
      • 2018-05-10
      • 2022-06-26
      • 1970-01-01
      • 2017-03-25
      相关资源
      最近更新 更多