【问题标题】:Generate big random sequence of unique numbers [duplicate]生成唯一数字的大随机序列[重复]
【发布时间】:2013-04-27 12:10:42
【问题描述】:

我需要在一个文件中填写大量由数字标识的记录(测试数据)。记录的数量很大,并且id应该是唯一的,记录的顺序应该是随机的(或伪随机的)。

我试过这个:

# coding: utf-8
import random

COUNT = 100000000

random.seed(0)
file_1 = open('file1', 'w')
for i in random.sample(xrange(COUNT), COUNT):
    file_1.write('ID{0},A{0}\n'.format(i))
file_1.close()

但它正在吞噬我所有的记忆。

有没有办法生成一个大的连续(不一定,但它会很好,否则是唯一的)整数的洗牌序列?使用生成器而不将所有序列保存在 RAM 中?

【问题讨论】:

  • @Blender,该方法不需要将所有元素存储在内存中吗?
  • @Dogbert:滚动浏览投票最多的答案。有一些可以解决内存问题。
  • 你真的有1亿个数字,还是这个问题更笼统?
  • 是的,我需要生成一个几千兆字节的测试记录文件
  • 数字应该有多大? 100mio 连续,或者更大的数字,比如完整的 32 位甚至 64/128 位好吗?

标签: python random


【解决方案1】:

如果问题中有 1 亿个数字,那么这实际上是可在内存中管理的(大约需要 0.5 GB)。

正如 DSM 指出的,这可以通过标准模块以有效的方式完成:

>>> import array
>>> a = array.array('I', xrange(10**8))  # a.itemsize indicates 4 bytes per element => about 0.5 GB
>>> import random                                                               
>>> random.shuffle(a)

也可以使用第三方 NumPy 包,它是用于高效管理数组的标准 Python 工具:

>>> import numpy
>>> ids = numpy.arange(100000000, dtype='uint32')  # 32 bits is enough for numbers up to about 4 billion
>>> numpy.random.shuffle(ids)

(这仅在您的程序已经使用 NumPy 时才有用,因为标准模块方法的效率差不多)。


这两种方法在我的机器上花费的时间大致相同(洗牌可能需要 1 分钟),但它们使用的 0.5 GB 对于当前的计算机来说并不算大。

PS:与使用的随机生成器的周期相比,洗牌的元素太多而无法真正随机,因为可能的排列太多了。换句话说,Python shuffle 的数量少于可能的 shuffle 数量!

【讨论】:

  • 即使没有numpy,我认为a = array.array('I', xrange(10**8))random.shuffle(a) 也会达到同样的目的。如果 N 足够小,这无疑是通往目标的最简单路线。
  • @DSM:很好,谢谢!
  • 我接受了您的回答 - 它有助于生成所需的数据。仍然很高兴看到生成器的答案。
  • 使用位向量库,您可以存储一组整数 0 <= x < 100 million 大约 12.5 兆字节 - 每个整数的存在或不存在由单个位指示。他们Python Package Index 中有一个位向量库 - 我想它支持所有需要的操作,但尚未检查。根据访问方式,位向量通常会很快,因为它很紧凑,但有时会很慢,因为位摆弄开销有时会超过内存带宽优势。
  • mersenne twister random 使用的周期为 2^19937 - 1(根据维基百科)。如果我没有摆弄 Wolfram alpha(它拒绝直接评估这些,所以我不得不使用斯特林的近似值),1 亿!大约是 2^(10^9.4),所以你的 PS 是正确的。这段时期仍然令人印象深刻。
【解决方案2】:

可能类似于(不会是连续的,但会是唯一的):

from uuid import uuid4

def unique_nums():  # Not strictly unique, but *practically* unique
    while True:
        yield int(uuid4().hex, 16)
        # alternative yield uuid4().int

unique_num = unique_nums()
next(unique_num)
next(unique_num) # etc...

【讨论】:

  • 看起来很简单!有没有办法重复有种子的序列?
  • 为了记录,这并不能保证唯一性,尽管它们在前 10^8 个数字中唯一的。它基本上只是采用非常大的随机数,然后观察没有冲突。
  • 这里是关于碰撞可能性的参考:en.wikipedia.org/wiki/…
  • @EOL 感谢该链接 - 非常有用的参考
【解决方案3】:

您可以通过阅读(在 linux 上)/dev/urandom 或使用 os.urandom()struct.unpack() 轻松获取随机整数:

返回适合加密使用的由 n 个随机字节组成的字符串。

此函数从特定于操作系统的随机源返回随机字节。返回的数据对于加密应用程序来说应该是不可预测的,尽管它的确切质量取决于操作系统的实现。在类 UNIX 系统上,这将查询 /dev/urandom,而在 Windows 上,它将使用 CryptGenRandom。如果未找到随机源,则会引发 NotImplementedError

>>> for i in range(4): print( hex( struct.unpack('<L', os.urandom(4))[0]))
... 
0xbd7b6def
0xd3ecf2e6
0xf570b955
0xe30babb6

而另一方面 random 包:

但是,由于是完全确定性的,它并不适合所有用途,并且完全不适合加密用途。

如果您真的需要独特记录,您应该使用thisanswer provided by EOL

但假设真正的来源是随机的,可能有重复的字符,您将有1/N(其中N = 2 ** sizeof(int)*8 = 2 ** 32)第一次猜测命中项目的机会,因此您可以获得(2**32) ** length 可能的输出。

另一方面当using just unique results you'll have max:

product from i = 0 to length {2*32 - i} 
               = n! / (n-length)!
               = (2**32)! / (2**32-length)!

! 是阶乘,而不是逻辑否定。所以你只会减少结果的随机性。

【讨论】:

  • 不幸的是,我真的需要它们是独一无二的。
  • @warwaruk 我很好奇为什么,但在这种情况下,请选择 EOL 的答案(虽然我真的不确定numpy 是如何处理密码学的)。
【解决方案4】:

这会让你的记忆保持正常,但可能会杀死你的磁盘:)

它生成一个数字序列从 0 到 100000000 的文件,然后随机选择其中的位置并写入另一个文件。必须在第一个文件中重新组织数字以“删除”已经选择的数字。

import random

COUNT = 100000000

# Feed the file
with open('file1','w') as f:
    i = 0
    while i <= COUNT:
        f.write("{0:08d}".format(i))
        i += 1

with open('file1','r+') as f1:
    i = COUNT
    with open('file2','w') as f2:
        while i >= 0:
            f1.seek(i*8)
            # Read the last val
            last_val = f1.read(8)
            random_pos = random.randint(0, i)
            # Read random pos
            f1.seek(random_pos*8)
            random_val = f1.read(8)
            f2.write('ID{0},A{0}\n'.format(random_val))
            # Write the last value to this position
            f1.seek(random_pos*8)
            f1.write(last_val)
            i -= 1
print "Done"

【讨论】:

  • 一个细节:你的while循环通常写成for … in xrange(…)循环。
  • 用于生成排列的有趣算法。如果您也用文字解释它会很有用:这将使您的答案更容易阅读。请注意,您生成的是 COUNT+1 数字而不是 COUNT。我还想指出,通过使用二进制表示而不是文本表示,它显然可以提高一点效率(磁盘使用的因素 2)。
  • stackoverflow.com/a/196065/42973有一个类似但更简单的方法(单个文件),有解释!
  • 感谢 cmets @EOL,我会添加一些解释。
猜你喜欢
  • 1970-01-01
  • 2012-12-29
  • 1970-01-01
  • 1970-01-01
  • 2013-04-22
  • 2016-05-24
  • 1970-01-01
  • 1970-01-01
  • 2018-05-03
相关资源
最近更新 更多