生成可种子化数据的随机字符串

5
我希望您能够提供一种在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兆字节的数据需要大约1.8秒。有没有改进它的方法(或者用os.urandom来生成随机种子)。


就你的问题而言,以下解释可能并不相关,但是 urandom 从系统噪音中获取其随机性(请参阅 Linux urandom 手册页),因此允许其播种没有意义。无法进行播种测试有时很烦人,但另一方面它成为了密码学目的的有用随机源。我想这并不是你的问题真正关心的部分,但至少可以解释为什么没有办法对其进行播种。 :) - PM 2Ring
4个回答

3
如果您有 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 字符串的。

那么,这个怎么样?

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的此答案
- PM 2Ring
嗯,没错。我没有正确地阅读那个问题。刚刚提供了一个新的答案! - cristianmtr
哇,使用 getrandbits 明显比我的解决方案更快。但是你为什么要在全局变量中做那些事情呢? - PM 2Ring
只是想在函数完成后能够检查长度和内容,不应该对性能产生影响。 - cristianmtr
他们现在将一个同名的方法randbytes添加到3.9版本了。顺便提一句,详情请查看:https://dev59.com/047ea4cB1Zd3GeqPEKlQ#66018128 - Ciro Santilli OurBigBook.com

1

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))

使用固定种子0向标准输出写入8个伪随机字节:

./main.py | hd

输出:

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

它在CPython中的定义非常简单:

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

这里将其转换为Bash一行命令,并与/dev/urandom进行基准测试比较:类似于可配置种子的/dev/urandom?


0

正如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(),但在 Python 3 中,bytes() 更好,因为 Python 3 字符串是 Unicode。

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