首先,我能够做到,但我对速度不满意。
我的问题是,有更好,更快的方法吗?
我有一个看起来像这样的项目列表:
[(1,2), (1,2), (4,3), (7,8)]
我需要获得所有独特的组合。例如,2个项目的唯一组合将是:
[(1,2), (1,2)], [(1,2), (4,3)], [(1,2), (7,8)], [(4,3), (7,8)]
使用itertools.combinations后,由于重复,我获得了更多。例如,我得到每个包含(1,2)两次的列表。如果我创建了一组这些组合,我会得到独特的组合。 当原始列表有80个元组并且我想要其中包含6个项目时,会出现问题。获得该设置需要超过30秒。如果我能得到这个数字,我会非常高兴。
我知道组合的数量很大,这就是为什么创建这个集合非常耗时的原因。但我仍然希望有一个库以某种方式优化了这个过程,加快了它的速度。
值得注意的是,从我发现的所有组合中,我只测试了前10000个左右。因为在某些情况下,所有组合都可能因处理太多而无法处理,所以我不想花太多时间在它们上面,因为还有其他测试要做。
这是我现在所拥有的样本:
from itertools import combinations
ls = [list of random NON-unique sets (x,y)]
# ls = [(1,2), (1,2), (4,3), (7,8)] # example
# in the second code snipped it is shown how I generate ls for testing
all_combos = combinations(ls, 6)
all_combos_set = set(all_combos)
for combo in all_combos_set:
do_some_test_on(combo)
如果你想测试它..这是我用来测试不同方法的速度:
def main3():
tries = 4
elements_in_combo = 6
rng = 90
data = [0]*rng
for tr in range(tries):
for n in range(1, rng):
quantity = 0
name = (0,0)
ls = []
for i in range(n):
if quantity == 0:
quantity = int(abs(gauss(0, 4)))
if quantity != 0:
quantity -= 1
name = (randint(1000,7000), randint(1000,7000))
ls.append(name)
else:
quantity -= 1
ls.append(name)
start_time = time.time()
all_combos = combinations(ls, elements_in_combo)
all_combos = set(all_combos)
duration = time.time() - start_time
data[n] += duration
print(n, "random files take", duration, "seconds.")
if duration > 30:
break
for i in range(rng):
print("average duration for", i, "is", (data[i]/tries), "seconds.")
答案 0 :(得分:2)
最初提出的问题"有更好,更快的方法吗?"实际上有两个问题:
我想缩小问题的答案"有更快的方式吗?"致:
是否有更快的方法从列表中删除重复项,如下所示:
lstWithUniqueElements = list(set(lstWithDuplicates))
据我所知,没有更快的方法......
现在让我们更多地关注问题的第二部分("有更好的方法吗?")。这通常很难,需要很多讨论来回答这类问题,但在这里情况并非如此,因为问题的作者(引用)已经明确说明了更好的方法:
我喜欢使用发电机功能。 itertools组合() 本身是一个可迭代的而不是列表或集合,所以如果我弄清楚如何 产生独特的组合,非常棒。
所以这就是:
def uniqueCombinations(lstList, comboSize):
from itertools import combinations
lstList.sort()
allCombos = combinations(lstList, comboSize)
setUniqueCombos = set()
for comboCandidate in allCombos:
if comboCandidate in setUniqueCombos:
continue
yield comboCandidate
setUniqueCombos.add(comboCandidate)
那是......
<小时/> 还有一件事可能值得一提。问题的作者选择了获取唯一组合的方法,以防它们生成的列表不仅具有唯一性,而且具有相同值的多个元素在某些特殊情况下也不起作用,如下所示:< / p>
set(combinations(['a','a','b','a'], 2)) gives: {('a', 'b'), ('b', 'a'), ('a', 'a')}
uniqueCombinations(['a','a','b','a'],2) gives: {('a', 'b'), ('a', 'a')}
在这两者之间有一个纯Python函数可用于stackoverflow,它比上面提供的更快更慢。它怎么能更快更慢?有关详细信息,请参阅HERE。
答案 1 :(得分:1)
我想这个答案在OP需要它之后就很好了,但是我遇到了同样的问题,我想贡献我的解决方案。我不希望在内存中存储任何组合,因为很容易看出这是怎么回事。
首先,this link非常清楚地说明了当元素重复时如何计算不同组合的数量。策略是创建带有替换的组合,然后丢弃不允许的组合。
例如,如果集合为(A,A,B,B),并且您想要3个元素的所有组合,则不允许组合(A,A,A)和(B,B,B)。因此,该想法是从原始集中的唯一元素列表中创建所有可能的替换组合,然后丢弃那些无效的组合。这不会占用任何查找空间,并且易于编写。
但是,当我们的布景包含许多独特元素时,此策略非常浪费。将这个问题推到极点,集合(A,B,C)中唯一的3个元素长的组合显然是(A,B,C),但是这种策略会产生(A,A,A),(A ,A,B),...为了缓解这一问题,可能会注意到唯一元素只能在有效组合中出现一次:对于唯一元素,标准itertools.combinations()可以执行。
因此,如果我们包含唯一元素和重复元素的混合,则最终组合可以分为使用itertools.combinations()从唯一元素生成的部分和通过itertools.combinations_with_replacement()生成的部分重复的元素。
总而言之,这是代码。它运行的速度实际上取决于原始集合中的重复次数。最糟糕的情况是没有重复的情况:
import itertools
from collections import Counter
#Check if an element is repeated more times than allowed.
def comb_check(comb, original_dic):
trouble = False
if not comb:
return(not trouble)
comb_unique = set(comb)
ratio = len(comb_unique)/len(comb)
if ratio < 1:
comb = Counter(comb)
ks = (v for v in comb_unique)
complete = False
while (not trouble) and (not complete):
try:
k = next(ks)
if comb[k] > 1:
if original_dic[k] < comb[k]: trouble = True
except StopIteration:
complete = True
return(not trouble)
def generate_comb(elements,k):
elements = Counter(elements)
elements_unique = [k for k,v in elements.items() if v == 1]
elements_other = [k for k, v in elements.items() if k not in elements_unique]
max_repetition = sum([elements[k] for k in elements_other ])
for n in range(0, min(k+1,len(elements_unique)+1)):
if (n + max_repetition)>= k:
for i in itertools.combinations(elements_unique, n):
for j in itertools.combinations_with_replacement(elements_other, k-n):
if comb_check(j, elements):
(yield final)
#All unique elements is the worst case when it comes to time
lst = [a for a in range(80)]
for k in generate_comb(lst, 6):
pass
#It took my machine ~ 264 sec to run this
#Slightly better
lst = [a for a in range(40)] + [a for a in range(40)]
for k in generate_comb(lst, 6):
pass
#It took my machine ~ 32 sec to run this