【问题标题】:How do I make a list of random unique tuples?如何制作随机唯一元组的列表?
【发布时间】:2020-03-11 17:06:05
【问题描述】:

我查看了几个与此问题类似的答案,似乎都有很好的 oneliner 答案,但仅处理通过删除重复项使列表独一无二的事实。我需要列表正好有 5 个。

我能想到的唯一代码是这样的:

from random import *

tuples = []

while len(tuples) < 5:
    rand = (randint(0, 6), randint(0,6))
    if rand not in tuples:
        tuples.append(rand)

我觉得有一种更简单的方法,但我想不通。我尝试从随机播放 sample():

sample((randint(0,6), randint(0,6)), 5)

但这给了我一个“样本大于总体或为负”的错误。

【问题讨论】:

  • 您是在寻找从一小部分(例如(randint(0, 6), randint(0,6)) 的 49 种组合)中进行选择还是在许多可能性中进行选择?你真的需要list(有序)还是set(无序)也可以?
  • 在我的程序中,该集合受限于一个参数.. 就像 randint(0, size)。不必订购。
  • 请提供完整的错误信息。顺便说一句,import * 通常是不好的做法。
  • 选择五个唯一的数字。然后选择其他五个数字,不一定是唯一的。通过将唯一列表中的一个数字(因此所有元组也是唯一的)与另一个列表中的一个数字组合来创建元组,按顺序处理两个列表。

标签: python python-3.x random tuples unique


【解决方案1】:

一种快速的方法是使用itertools.product 生成所有元组可能性,然后使用sample 从中选择5 个:

from itertools import product
from random import sample
sample(list(product(range(7), repeat=2)), k=5)

【讨论】:

    【解决方案2】:

    对于这么小的一组输入,只需生成所有可能的输出,然后sample它们:

     import itertools
     import random
    
     size = 6
     random.sample(list(itertools.product(range(size+1), repeat=2)), 5)
    

    您指出边界 (size) 可能是一个参数,如果边界可能更大一点,这可能是一个问题(您将生成 size ** 2 tuples 来选择5,内存使用可能会失控)。如果这是一个问题,假设您只需要一对整数,那么有一个便宜的技巧:选择 一个 随机整数来编码两个结果整数,然后对其进行解码。例如:

    size = 6
    raw_sample = random.sample(range((size + 1) ** 2), 5)
    decoded_sample = [divmod(x, size+1) for x in raw_sample)]
    

    由于range 的开销为零(内存使用量不取决于长度),您可以从中精确选择五个值,开销与所选的五个成比例,而不是 49 个可能的结果。然后,您可以根据单个值的范围(在本例中为 0 到 size,因此 size + 1 可能的值)计算商和余数,这样就可以很便宜地得到高低结果。

    性能差异很明显;比较:

    def unique_random_pairs_by_product(size):
        return random.sample(list(itertools.product(range(size+1), repeat=2)), 5)
    

    到:

    def unique_random_pairs_optimized(size):
        val_range = size + 1
        return [divmod(x, val_range) for x in random.sample(range(val_range * val_range), 5)]
    

    optimized 版本即使是 6 的参数也减少了大约 15% 的时间(product 约为 4.65 μs,optimized 约为 3.95 μs)。但是在6 中的size,您根本看不到比例因子。对于size=100optimized 仅增加到~4.35 μs(时间略有增加,因为较大的range 更有可能必须分配新的ints,而不是使用小的int 缓存),而product 跃升至 387 μs,相差近 100 倍。对于size=1000product 的时间跳到 63.8 ms,而optimized 保持在 ~4.35 μs;运行时间相差 10,000 倍(内存使用率甚至更高)。如果size 变得比这更大,基于product 的解决方案将很快达到人类可以察觉到单个采样的延迟的程度; optimized 解决方案将继续以相同的性能运行(模数 divmod 的成本差异非常小)。

    【讨论】:

    • 确实是很好的解决方案 (+1),尽管如果尺寸真的很大,使用 sample 本身可能效率低下,在这种情况下,您可以考虑我的其他答案作为解决方案。
    • @blhsing:它本质上是如何低效的? “大量人口,小样本”的实现与您编写的内容非常相似;它创建一个set,并不断生成新索引并添加它们,直到它具有所需数量的唯一样本索引,然后查找所述唯一索引。
    • 哎呀,我没有意识到 random.sample 的大型 range 对象的实现确实是通过 while 循环完成的,直到我阅读了源代码。谢谢。
    猜你喜欢
    • 1970-01-01
    • 2019-08-24
    • 1970-01-01
    • 2018-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-19
    • 2013-04-22
    相关资源
    最近更新 更多