一种制作大型随机字节数组的有效方法

时间:2011-08-12 17:32:08

标签: python random bytearray

我需要创建一个特定大小的大型字节,但在运行时之前不知道大小。字节需要相当随机。字节数大小可以小到几KB,但可以大到几MB。我不想逐字节迭代。这太慢了 - 我需要与numpy.random类似的性能。但是,我没有可用于此项目的numpy模块。是否有标准python安装的一部分可以做到这一点?或者我需要compile my own using C

对于那些要求时间的人:

>>> timeit.timeit('[random.randint(0,128) for i in xrange(1,100000)]',setup='import random', number=100)
35.73110193696641
>>> timeit.timeit('numpy.random.random_integers(0,128,100000)',setup='import numpy', number=100)
0.5785652013481126
>>> 

4 个答案:

答案 0 :(得分:37)

即使在Windows上,os模块也提供urandom

bytearray(os.urandom(1000000))

这似乎可以根据您的需要快速执行,事实上,我的时间比你的numpy更好(虽然我们的机器可能会大不相同):

timeit.timeit(lambda:bytearray(os.urandom(1000000)), number=10)
0.0554857286941

答案 1 :(得分:6)

包括numpy有什么问题?无论如何,这会创建一个随机的N位整数:

import random
N = 100000
bits = random.getrandbits(N)

因此,如果你需要查看是否设置了第j位的值,你可以bits & (2**j)==(2**j)

编辑:他要求字节数组不是位数组。奈德的答案更好:your_byte_array= bytearray((random.getrandbits(8) for i in xrange(N))

答案 2 :(得分:4)

有几种可能性,有些可能比os.urandom快。还要考虑是否必须从随机种子确定性地生成数据。这对于必须重现故障的单元测试非常有用。

简短而精辟:

lambda n:bytearray(map(random.getrandbits,(8,)*n))

我已经将上述内容用于单元测试并且速度足够快但可以更快完成吗?

使用itertools:

lambda n:bytearray(itertools.imap(random.getrandbits,itertools.repeat(8,n))))

itertools和struct每次迭代生成8个字节

lambda n:(b''.join(map(struct.Struct("!Q").pack,itertools.imap(
    random.getrandbits,itertools.repeat(64,(n+7)//8)))))[:n]

基于b''.join的任何内容都将填充最终bytearray与临时对象消耗的内存的3-7倍,因为它在将所有子字符串连接在一起之前将所有子字符串排队,并且python对象具有lots of storage overhead。 / p>

使用专门的函数生成大块可以提供更好的性能并避免填充内存。

import random,itertools,struct,operator
def randbytes(n,_struct8k=struct.Struct("!1000Q").pack_into):
    if n<8000:
        longs=(n+7)//8
        return struct.pack("!%iQ"%longs,*map(
            random.getrandbits,itertools.repeat(64,longs)))[:n]
    data=bytearray(n);
    for offset in xrange(0,n-7999,8000):
        _struct8k(data,offset,
            *map(random.getrandbits,itertools.repeat(64,1000)))
    offset+=8000
    data[offset:]=randbytes(n-offset)
    return data

效果

  • .84 MB / s randint原始解决方案:
  • 4.8 MB / s bytearray(getrandbits(8) for _ in xrange(n)) :(其他海报解决方案)
  • 6.4MB / s bytearray(map(getrandbits,(8,)*n))
  • 7.2 MB / s itertoolsgetrandbits
  • 10 MB / s os.urandom
  • 23 MB / s itertoolsstruct
  • 35 MB / s :优化功能(len = 100MB ... 1KB)

注意:所有测试都使用10KB作为字符串大小。结果一直持续到中间结果填满记忆。

注意:os.urandom旨在提供安全的随机种子。应用程序使用自己的快速PRNG扩展种子。这是一个例子,在计数器模式下使用AES作为PRNG:

import os
seed=os.urandom(32)

from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
backend = default_backend()
cipher = Cipher(algorithms.AES(seed), modes.CTR(b'\0'*16), backend=backend)
encryptor = cipher.encryptor()

nulls=b'\0'*(10**5) #100k
from timeit import timeit
t=timeit(lambda:encryptor.update(nulls),number=10**5) #1GB, (100K*10k)
print("%.1f MB/s"%(1000/t))

这会产生 180 MB / s 的伪随机数据。 (没有硬件AES加速,单核)只有 ~5x 上面纯python代码的速度。

附录

有一个纯python加密库等待编写。将上述技术与hashlib和流密码技术结合起来看起来很有希望。这是一个预告片,一个快速的字符串xor( 42MB / s )。

def xor(a,b):
    s="!%iQ%iB"%divmod(len(a),8)
    return struct.pack(s,*itertools.imap(operator.xor,
        struct.unpack(s,a),
        struct.unpack(s,b)))

答案 3 :(得分:3)

import random
def randbytes(n):
    for _ in xrange(n):
        yield random.getrandbits(8)

my_random_bytes = bytearray(randbytes(1000000))

在itertools中可能有一些东西可以帮到这里,总有......

我的时间表明这比[random.randint(0,128) for i in xrange(1,100000)]

快了五倍