从高层次上讲,我正在尝试从列表中n个项目的所有组合中采样n_samples个项目。在n的值较小且列表长度较小时(n <= 5,len(list)<75),这很好-我只是使用itertools生成组合,转换为列表,然后使用random.sample随机采样正确的数字。
但是,我的用例要求我生成组合,随机采样几千个元素,然后从列表中删除其中一个组合,然后从较小的列表重新开始。
这会在n和len(list)的值较高时产生一个问题-具有120个列表项且n = 5,这种用例意味着我必须进行多次列表转换,因此受生成器的时间限制- >列出具有约1.9亿个项目的发电机的列表转换。这将花费非常长的时间(对于某些较差的示例,将花费超过20分钟的时间)。
用例不需要统计上统一的样本或任何东西,而我纯粹是使用样本,因为使用高n和长列表进行处理,每种可能的组合在计算上都是不切实际的,并且快速处理非常重要。
我切换到使用iterator.islice方法,仅从生成器中获取前n_samples个项目并使用它们。这极大地提高了速度(本来耗时20分钟的示例现在需要34秒),但是性能却受到了打击。我认为这是由于itertools如何生成组合-例如,
list(itertools.combinations(list(range(4)), 2))
产生以下列表: [(0,1),(0,2),(0,3),(1,2),(1,3),(2,3)]
因此,如果我有一个足够长的列表和一个足够大的n,则仅通过将它们从生成器中拉出就对100,000+个项目进行采样将导致100,000+个项目,其中第一个元素相同,这并不理想。如我所说,我不需要完美的随机抽样,但是我认为使用此方法而不是对整个列表进行随机抽样会导致性能崩溃。
基本上,我需要一种有效的方法,从长度为n的所有可能组合(其中n通常在2-8左右的范围内)中有效采样n_samples个项(其中n_samples在10k至500k之间)。长度范围从〜20到〜200。
非常感谢您可以提供的任何建议或资源!
答案 0 :(得分:1)
根据您的描述,我相信如果您随机选择每个组件(独立于其他组件)并继续进行操作,直到获得必要的样本,您将拥有一个效率更高的算法。 RNG(随机数生成器)非常快,足以弥补需要替换偶发副本的麻烦。将选择的组合存储为一组元组(可哈希),然后您可以在恒定时间内查找集合包含物,从而使集合成为线性时间。像这样:
from random import randint
# For illustration, the "lsits" include letters, symbols, 3-letter words, and low primes
list1 = "pythonic"
list2 = "~!@#$%^&*()"
list3 = ["dog", "cat", "ape", "red", "cwm", "pox"]
list4 = [2, 3, 5, 7, 11, 13, 17, 19]
combo = [list1, list2, list3, list4]
my_sample = set()
needed_size = 10
while len(my_sample) < needed_size:
# Choose one random item from each list; that forms an element
elem = tuple([comp[randint(0, len(comp)-1)] for comp in combo])
# Using a set elminates duplicates easily
my_sample.add(elem)
print(my_sample)
输出:
{('h', '$', 'pox', 7),
('y', '(', 'cat', 11),
('n', '@', 'cat', 7),
('i', '^', 'ape', 13),
('y', '#', 'pox', 11),
('o', '%', 'dog', 7),
('p', '^', 'cwm', 13),
('c', '*', 'dog', 19),
('o', ')', 'pox', 11),
('h', '~', 'cat', 5)}
另一种可能性是在长度乘积(在这种情况下为8 * 10 * 6 * 8)的乘积范围内生成一个随机数,然后使用整数除法和{{1} },将其分为四个随机索引。
另一种可能性是简单地生成您的第一组随机索引,然后根据需要增加这些随机索引,依次浏览列表。在这种情况下,您将希望列表长度是成对的相对素数。您可以根据需要通过添加mod
元素来保证这一点。带有None
的任何组合都将被丢弃。
这些想法会让您感动吗?