簡體   English   中英

根據集修剪組合列表

[英]Prune a list of combinations based on sets

雖然這個問題是使用Python編程語言制定的,但我認為它更多的是編程邏輯問題。

我有一個所有可能組合的列表,即:n選擇k

我可以使用准備這樣的列表

import itertools
bits_list = list(itertools.combinations(range(n), k))

如果'n'為100,且'k'為5,則'bits_list'的長度為75287520。

現在,我想修剪這個列表,這樣數字就會出現在組中,或者它們不會出現。 我們以下面的集合為例:

設置1:[0,1,2]
第2集:[57,58]
第3集:[10,15,20,25]
第4集:[10,11,12,13,14,15,16,17,18,19]

這里每個集合需要一起出現在bits_list的任何成員中,或者根本不出現。

到目前為止,我只能想到一個解決這個問題的蠻力if-else方法,但if-else條件的數量會非常大。

這就是我所擁有的:

bits_list = [x for x in list(itertools.combinations(range(n), k)) 
             if all(y in x for y in [0, 1, 2]) or
             all(y not in x for y in [0, 1, 2])]

現在,這只涵蓋了Set 1.我想為很多套這樣做。 如果集合的長度長於k的值,我們可以忽略該集合(例如,k = 5和Set 4)。

注意,最終目標是讓'k'迭代一個范圍,比如說[5:25]並對附加列表進行處理。 列表的大小在這里呈指數級增長,從計算上講,非常昂貴!

當'k'為10時,python解釋器在完成任何普通筆記本電腦16 GB RAM之前中斷該過程。 我需要找到一個適合相對現代服務器(不是集群或服務器場)的內存的解決方案。

任何幫助是極大的贊賞!

PS:直觀地說,將此問題視為為登上公共巴士或火車系統的人提供所有可能的情況。 通常,您登上整個團體,或者您沒有登上任何人。


更新:

  1. 對於上面給定的集合,如果k = 5,則bits_list的有效成員將是[0,1,2,57,58],即:Set1和Set2的組合。 如果k = 10,那么我們可以將Set1 + Set2 + Set3 + NoSetElement構建為可能的成員。 @DonkeyKong的解決方案讓我意識到我沒有在我的問題中明確提到這一點。

  2. 我有很多套; 我打算使用足夠的集合來修剪完整的組合列表,以使bits_list最終適合內存。

  3. @ 9000的建議在這里完全有效,在每次迭代中,我可以將組合保存為實際位。

在某一點(大約n = 90,k = 5),這仍然會被內存錯誤( 我不知道你是如何從列表中躲避)中摧毀,但它比你目前的實施。 對於n=80k=5 ,我的基本基准測試使我的解決方案在2.6秒,而你的解決方案在52秒左右。

我們的想法是分別構建過濾器的不相交和子集部分。 不相交的部分是微不足道的,子集部分是通過采用長度為k - set_len的所有不相交組合的itertools.product和你的集合的各個元素來計算的。

from itertools import combinations, product, chain
n = 80
k = 5
set1 = {0,1,2}

nots = set(range(n)) - set1
disj_part = list(combinations(nots, k))
subs_part = [tuple(chain(x, els)) for x, *els in 
              product(combinations(nots, k - len(set1)), *([e] for e in set1))]
full_l = disj_part + subs_part

如果您實際將比特表示為比特,即在整數n位長度的二進制表示中將0/1值設置為恰好設置k位,則您需要存儲數據的RAM量將大大減小。

此外,您還可以使用位操作來檢查mask中的所有位是否實際設置( value & mask == mask ),還是全部未設置( value | ~mask == value )。

蠻力可能會花費更短的時間來考慮更聰明的算法,因此對於一次性過濾來說完全沒問題。

如果您必須經常快速地執行此操作,並且您的n是小數百或更少,我寧願使用cython來有效地描述蠻力算法,而不是查看算法改進。 現代CPU可以有效地操作64位數字; 你不會因為不比較一部分數字而受益匪淺。

OTOH如果您的n非常大,並且要比較的集合數量也很大,您可以對比特進行分區以進行有效比較。

假設您可以有效地比較64位的塊,並且您的位列表每個包含例如100個塊。 然后你可以對字符串執行相同的操作:將chunk與chunk進行比較,如果其中一個塊無法匹配,則不要比較其余部分。

更快的實現是替換以下的if和all()語句:

bits_list = [x for x in list(itertools.combinations(range(n), k)) 
             if all(y in x for y in [0, 1, 2]) or
             all(y not in x for y in [0, 1, 2])]

使用python的set操作isdisjoint()issubset()操作。

bits_generator = (set(x) for x in itertools.combinations(range(n), k))
first_set = set([0,1,2])
filter_bits = (x for x in bits_generator 
             if x.issubset(first_set) or
             x.isdisjoint(first_set))
answer_for_first_set = list(filter_bits)

我可以繼續使用發電機和發電機,你不會耗盡內存,但你會等待並加速宇宙的熱量死亡。 不是因為python的運行時或其他實現細節,而是因為如果你選擇一個大的N和K值,即使在計算機時間內也存在一些不可行的問題。

基於@Mitch回答的想法,我創建了一個解決方案,其思路略微不同於最初在問題中提出的解決方案。 我沒有創建所有組合的列表( bits_list ),然后修剪那些與列出的集合不匹配的組合, bits_list從集合中構建了bits_list

import itertools
all_sets = [[0, 1, 2], [3, 4, 5], [6, 7], [8], [9, 19, 29], [10, 20, 30], 
            [11, 21, 31], [12, 22, 32], ...[57, 58], ... [95], [96], [97]]
bits_list = [list(itertools.chain.from_iterable(x)) for y in [1, 2, 3, 4, 5] 
             for x in itertools.combinations(all_sets, y)]

在這里,我沒有找到n選擇k,然后循環查找所有k,並找到與集合匹配的組合,而是從集合開始,甚至將各個成員自己包含在集合中,因此不再需要2個組件 - 不相交和子集部分 - 在@Mitch的答案中討論過。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM