【问题标题】:Generating random string of seedable data生成可种子数据的随机字符串
【发布时间】:2015-09-01 10:32:41
【问题描述】:

我正在寻找一种在 Python 中生成 n 个字节的随机字符串的方法,该方法类似于os.urandom() 方法,只是提供了一种数据生成种子的方法。

到目前为止我有:

def genRandData(size):
    buf = chr(random.randint(0,255))
    for i in range(size-1):
        buf = buf + chr(random.randint(0,255))
    return str(buf)

但是这个函数很慢,在我的机器上生成一兆字节的数据大约需要 1.8 秒。有什么方法可以改善这一点(或者另一种方法是播种 os.urandom)。

【问题讨论】:

  • FWIW,urandom 从随机系统噪声中获得随机性(请参阅the Linux man page for urandom),因此允许它可播种是没有意义的。缺乏可种子性对于测试事物来说很烦人,但 OTOH 使它成为加密目的的有用随机性来源。我想这与您的问题并不真正相关,但至少它解释了为什么没有办法播种它。 :)

标签: python random


【解决方案1】:

如果您有可用的numpy,它有一个random 模块版本numpy.random,其中包含您可能会考虑的此功能:

numpy.random.bytes(length)

速度很快:

$ python -mtimeit "import numpy" "numpy.random.bytes(1<<30)"
10 loops, best of 3: 2.19 sec per loop

这是 1GiB。

你可以用numpy.random.seed播种它。

【讨论】:

    【解决方案2】:

    新答案

    重新阅读 OP 的问题后,我现在明白这是关于原始字节,而不是 ascii chars 字符串

    那么,这个怎么样?

    import random
    gl = 0
    def randBytes(size):
        global gl
        nr = bytearray(random.getrandbits(8) for _ in xrange(size))
        gl = nr
        return
    
    %timeit randBytes(1000000)
    1 loops, best of 3: 262 ms per loop
    
    In [27]: gl.__sizeof__()
    Out[27]: 1087223
    

    老答案在这里

    import random
    import string
    def generateRandomString(size):
        return(''.join(random.choice(string.ascii_letters) for i in range(size)))
    

    注意事项:

    一个 ascii 字符是 1 个字节。所以“size”既表示字符串的长度,也表示字节的大小。

    您可以使用 string.ascii_uppercase 或 ascii_lowercase 来区分大小写

    random.seed 可用于指定种子。

    random.seed([x])¶

    初始化基本随机数生成器。可选参数 x 可以 是任何可散列的对象。如果省略 x 或 None,则为当前系统时间 用来;当前系统时间也用于初始化生成器 首次导入模块时。如果提供随机源 由操作系统使用,而不是系统时间(参见 os.urandom() 函数了解可用性的详细信息)。

    所以你可以:

        import random
        import string
        def generateRandomString(size, seed=None):
            if seed != None:
                 random.seed(seed)
            return(''.join(random.choice(string.ascii_letters) for i in range(size)))
    

    时间安排:

    In [30]: %time generateRandomString(1000000)
    Wall time: 554 ms
    <and then output>
    

    【讨论】:

    • 1) OP 想要从\x00\xff 的所有可能的字节值,而不仅仅是字母。 2) 在列表推导中使用.join() 比在生成器表达式上更有效,因为.join() 必须扫描它加入的字符串两次:第一次确定总长度,第二次将字符串复制到目标缓冲区。有关详细信息,请参阅 Python 核心开发人员 Raymond Hettinger 的this answer
    • 嗯,对。没有正确阅读。刚刚提供了一个新答案!
    • 哇,使用getrandbits 肯定比我的解决方案快。但是你为什么要用全局变量做这些事情呢?
    • 只是希望能够在函数完成后检查长度和内容。不应该影响性能本身
    • 他们现在在 3.9 中添加了一个同名的方法 randbytes 顺便说一句:stackoverflow.com/a/66018128/895245
    【解决方案3】:

    正如 Dan D. 所说,让 numpy 以 C 速度一次性生成字节将比以 Python 速度一次生成一个字节快方式

    但是,如果您不想使用numpy,您可以让您的代码更高效一些。

    通过连接(例如buf = buf + chr(random.randint(0,255)))构建字符串非常慢,因为必须在每个循环上分配一个新的buf(请记住,Python 字符串是不可变的)。 Python 中从子字符串构建字符串的常用技术是将子字符串累积到一个列表中,然后使用str.join() 方法一次性将它们组合起来。

    我们还可以通过预先生成 1 字节字符串列表而不是为我们想要的每个字节调用 chr() 来节省一点时间。

    from random import seed, choice
    
    allbytes = [chr(i) for i in range(256)]
    
    def random_bytes(n):
        bytes = []
        for _ in range(n):
            bytes.append(choice(allbytes))
        return ''.join(bytes)
    

    我们可以通过使用列表推导来简化它并稍微提高效率:

    def random_bytes(n):
        return ''.join([choice(allbytes) for _ in range(n)])
    

    根据您打算如何使用这些随机字节,您可能会发现将它们放入 bytearraybytes 对象很有用。

    这是基于 cristianmtr 的新答案的变体:

    def random_bytes(n):
        return bytes(bytearray(getrandbits(8) for _ in xrange(n)))
    

    可以使用str() 代替bytes(),但bytes() 更适合Python 3,因为Python 3 字符串是Unicode。

    【讨论】:

      【解决方案4】:

      Python 3.9 random.randbytes + random.seed

      文档:https://docs.python.org/3.9/library/random.html#random.randbytes

      main.py

      #!/usr/bin/env python
      import random
      import sys
      random.seed(0)
      sys.stdout.buffer.write(random.randbytes(8))
      

      将 8 个伪随机字节写入标准输出,固定种子为 0:

      ./main.py | hd
      

      输出:

      00000000  cd 07 2c d8 be 6f 9f 62                           |..,..o.b|
      00000008
      

      它的definition in CPython 很简单:

          def randbytes(self, n):
              """Generate n random bytes."""
              return self.getrandbits(n * 8).to_bytes(n, 'little')
      

      在这里,它被转换为 Bash one 班轮,并与 /dev/urandom 进行了基准比较:Something similar to /dev/urandom with configurable seed?

      【讨论】:

        猜你喜欢
        • 2011-09-26
        • 2021-03-23
        • 1970-01-01
        • 1970-01-01
        • 2017-08-01
        • 2011-06-30
        • 2018-01-25
        相关资源
        最近更新 更多