【问题标题】:Avoid generating duplicate values from random避免从随机生成重复值
【发布时间】:2017-12-26 17:54:07
【问题描述】:

我想生成随机数并将它们存储在一个列表中,如下所示:

alist = [random.randint(0, 2 ** mypower - 1) for _ in range(total)]

我关心的是:我想在(0, 2 ** mypower - 1) 的范围内生成total=40 million 值。如果mypower = 64,则alist 的大小约为20GB (40M*64*8),这对于我的笔记本电脑内存来说非常大。我有一个想法来迭代生成大量值,比如一次 500 万个,并将它们保存到一个文件中,这样我就不必一次生成所有 40M 值。我担心的是,如果我在循环中执行此操作,可以保证random.randint(0, 2 ** mypower - 1) 不会生成之前迭代中已经生成的值?像这样的:

        for i in range(num_of_chunks):
            alist = [random.randint(0, 2 ** mypower - 1) for _ in range(chunk)]
            # save to file

【问题讨论】:

  • 不,完全保证!
  • 在单块情况下甚至不会出现这种情况。
  • random.randint 随机返回值。 random.randint(x, y) 每次调用它时都有相同的机会返回 x, y 范围内的任何数字。您可以计算得到重复的机会,考虑到您所采样的大小,我认为这会非常低,但如果您想完全避免重复,则需要使用其他东西。
  • 为什么是“*68*8”?我很确定你高估了这一点。
  • 你到底是怎么得到这个号码的?一个列表每个指针将占用 8 个字节(在 64 位 CPython 上),因此列表本身将占用超过 300MB 的空间,整数本身将占用 24 个字节,因此额外的 915MB 给予或占用 - 或约 1.2GB 的内存对于这样的清单。这是纯 Python,您可以使用更优化和简化的库在内存中塞入更多数据。

标签: python random


【解决方案1】:

生成不重复的随机值的一种方法是首先创建一个连续值列表

l = list(range(1000))

然后shuffle它:

import random
random.shuffle(l)

您可以多次这样做,然后将其保存在一个文件中,但是您的范围有限,因为您的内存有限,您永远看不到整个图片(这就像尝试对一个大列表进行排序而没有记忆)

正如有人指出的那样,要获得广泛的随机数,您需要大量内存,如此简单但效率不高。

我刚刚想到的另一个技巧:执行与上述相同的操作,但使用 step 生成范围。然后在第二遍中,将随机 offset 添加到值中。即使偏移值重复,也保证不会两次生成相同的数字:

import random

step = 10

l = list(range(0,1000-step,step))
random.shuffle(l)
newlist = [x+random.randrange(0,step) for x in l]

具有所需的最大值和迭代次数:

import random

number_of_iterations = 40*10**6
max_number = 2**64
step = max_number//number_of_iterations

l = list(range(0,max_number-step,step))
random.shuffle(l)
newlist = [x+random.randrange(0,step) for x in l]
print(len(newlist),len(set(newlist)))

在我的笔记本电脑上运行 1-2 分钟,并给出 40000000 个不同的值(均匀分布在整个范围内)

【讨论】:

  • 所以,他担心大小为 4000 万的列表对于他的 PC 来说太大了,而您的解决方案涉及大小为 18 quintillion 的列表?
  • 我不知道您如何计算内存大小,但我同意即使我的解决方案确保没有数字重复,您也需要将每个数字存储到最大限制。好吧,给我另一个想法。已编辑。
  • 你的意思是你不知道我是怎么得到 18 quintillion 的?那是 2**64,他的人口规模。
  • @Jean-FrançoisFabre - 他可能通过以下事实来计算大小:使用您的(第一种)方法,您必须生成一个包含所有需要的 64 位数字的列表,至少暂时(在改组和切断 40M 切片之前)- 2^64 个索引或〜18 quintillion(或就实际内存使用而言,大约 2^64 * 35 字节或 645EB 内存)。
  • 你们都是对的。从棋盘和米粒的故事我知道 2**64 是一个很大的数字……但我从不在乎实际值。我已经编辑了我的答案,现在可以在没有太多内存的情况下使用。
【解决方案2】:

好吧,既然效率/速度无关紧要,我认为这会起作用:

s = set()
while len(s) < total:
    s.add(random.randint(0, 2 ** mypower - 1))
alist = list(s)

由于集合中只能有独特的元素,我认为这会很好用

【讨论】:

  • 不客气。我确信有更好的方法可以做到这一点,但我想不出任何方法。
  • 使用set 很好,但是您插入的数字越多,速度就越慢,因为您不知道要执行多少次迭代才能达到总数。也就是说,random 不会重复太多,所以这是一个很好的折衷方案。
  • 是的,我确信有更有效的方法,我发布了这个方法,因为 op 说生成列表需要多长时间并不重要
  • @Jean-FrançoisFabre 它可能需要正好 4000 万次迭代。因为它可能无论如何都不会创建任何重复项。
  • @Jean-FrançoisFabre 好的,我计算过了。无论如何都不生成重复的概率约为 99.9956%。
【解决方案3】:

为了保证唯一值,您应该避免使用random。相反,您应该使用加密。因为加密是可逆的,所以在给定相同密钥的情况下,唯一的输入保证了唯一的输出。加密数字 0, 1, 2, 3, ... 如果您使用安全加密,您将得到保证的唯一看似随机的输出。良好的加密旨在提供看似随机的输出。

跟踪关键(必要的)以及你已经走了多远。对于您的第一批加密整数 0..5,000,000。对于第二批加密 5,000,001..10,000,000 等等。

您需要 64 位数字,因此请在 ECB 模式下使用 DES。 DES 是 64 位密码,因此每次加密的输出都是 64 位。 ECB 模式确实有一个弱点,但这仅适用于相同的输入。您正在提供独特的输入,因此该弱点与您的特定应用程序无关。

如果您需要重新生成相同的数字,只需使用相同的密钥重新加密它们即可。如果您需要一组不同的随机数(这将与第一组重复一些),则使用不同的密钥。唯一性保证仅适用于固定密钥。

【讨论】:

  • 为什么还要用这个分批做呢?
  • 该问题讨论了对数字进行批处理以帮助避免过大的数据块。这种方法避免了在批次之间交叉检查重复:可以根据需要生成唯一的“随机”数字,最多 2^64 - 1。
  • 我不同意“你应该避免使用random”的笼统说法(毕竟,random.sample 正是这个功能,它确实使用了random)。但无论如何我都会为创造力投票,因为我认为它在这种情况下有效(2^64 相当特别)。
  • 随机不能“保证唯一”。事实上,任何好的 RNG 都应该能够在小跑中两次返回相同的数字。这就是为什么当需要唯一性时,加密或洗牌通常会更好。有一些方法可以绕过 2^64(或 2^128 用于 AES)边界:使用格式保留加密将数字保持在所需范围内。
  • 这就是random.sample 使用“单独随机”的原因。你能指出一个格式保留加密算法,如果范围是 [0, 10^19) 而不是 [0, 2^64),它会在这里很好地工作吗? (大小差不多,只是不是 2 的幂。)最好是一个简单的,这样我就能理解 :-)
【解决方案4】:

它可能需要高 CPU 和物理内存! 我建议您对数据进行分类。 例如,您可以保存: 所有以 10 开头的数字为 5 个字符(例如:10365)到 10-5.txt

所有以 11 开头且为 6 个字符的数字(例如:114567)到 11-6.txt

然后检查一个新号码: 例如我的号码是 9256547 它以 92 开头,共 7 个字符。 所以我在 92-7.txt 中搜索此号码,如果不重复,我会将其添加到 92-7.txt 最后,您可以将所有文件连接在一起。 对不起,如果我有错误。我的主要语言不是英语。

【讨论】:

    【解决方案5】:

    通常随机数生成器根本不是真正随机的。事实上,这在某些情况下非常有用。如果您希望在第二次迭代后值是唯一的,请发送一个不同的种子值。

    随机种子()

    相同的种子将生成相同的列表,因此如果您希望下一次迭代相同,请使用相同的种子。如果您希望它与众不同,请使用不同的种子。

    【讨论】:

      猜你喜欢
      • 2014-02-05
      • 2019-06-15
      • 1970-01-01
      • 2023-03-23
      • 1970-01-01
      • 2018-12-01
      • 1970-01-01
      • 2010-12-19
      • 1970-01-01
      相关资源
      最近更新 更多