Python 从非唯一的项目列表中获取唯一的组合更快?

Python 从非唯一的项目列表中获取唯一的组合更快?,python,python-3.x,combinations,Python,Python 3.x,Combinations,首先,我能做到,但我对速度不满意 我的问题是,有没有更好更快的方法 我有一个如下所示的项目列表: [(1,2), (1,2), (4,3), (7,8)] 我需要得到所有独特的组合。例如,两个项目的独特组合为: [(1,2), (1,2)], [(1,2), (4,3)], [(1,2), (7,8)], [(4,3), (7,8)] 在使用itertools.combines之后,我得到了比这多得多的东西,因为它们是重复的。例如,每个列表包含(1,2)两次。如果我创建一组这些组合,我会得到

首先,我能做到,但我对速度不满意

我的问题是,有没有更好更快的方法

我有一个如下所示的项目列表:

[(1,2), (1,2), (4,3), (7,8)]
我需要得到所有独特的组合。例如,两个项目的独特组合为:

[(1,2), (1,2)], [(1,2), (4,3)], [(1,2), (7,8)], [(4,3), (7,8)]
在使用itertools.combines之后,我得到了比这多得多的东西,因为它们是重复的。例如,每个列表包含(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.")

最初提出的问题“有更好、更快的方法吗?”实际上有两个问题:

  • 有没有更快的办法
  • 有更好的办法吗
我想把“有没有更快的方法?”这个问题的答案缩小到:

是否有一种从列表中删除重复项的更快方法,方法如下:

lstWithUniqueElements=list(set(lstWithDuplicates))

?

据我所知,没有比这更快的方法

现在,让我们更专注于问题的第二部分(“有更好的方法吗?”)。回答此类问题通常非常困难,需要进行大量讨论,但这里的情况并非如此,因为问题的作者已经明确说明了更好的方法(引文):

我想用一个发电机函数。itertools组合() 它本身是一个iterable,而不是一个列表或集合,所以如果我知道如何 产生独特的组合,这将是伟大的

这就是:

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)
就这样。。。
还有一件事也许值得一提。问题的作者选择了一种获得唯一组合的方法,以防生成它们的列表不仅具有唯一性,而且具有相同值的多个元素在某些特殊情况下不起作用,例如:

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')}

在这两者之间,stackoverflow上有一个纯Python函数,它与上面提供的函数一样,速度更快,速度也更慢。它怎么能更快更慢呢?有关详细信息,请参阅

我想这个答案是在OP需要它之后出现的,但我遇到了同样的问题,我想提出我的解决方案。我不想在内存中存储任何组合,因为很容易看出这是如何出错的

首先,对重复元素时如何计算不同组合的数量提供了非常清晰的解释。策略是创建带有替换的组合,然后丢弃不允许的组合

例如,如果集合是(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.combines()就可以了

因此,如果我们混合了唯一元素和重复元素,那么最终的组合可以拆分为使用itertools.combines()从唯一元素生成的部分和使用重复元素的itertools.combines_和替换()从itertools.combines生成的部分

总而言之,这就是代码。它的运行速度实际上取决于原始集合中的重复量。最坏的情况是不重复的情况:

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  j)

#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
导入itertools
从收款进口柜台
#检查元素的重复次数是否超过允许的次数。
def梳齿检查(梳齿、原始dic):
麻烦=错误
如果不是梳子:
返回(不麻烦)
comb_unique=集合(comb)
比率=透镜(梳状物)唯一性/透镜(梳状物)
如果比率<1:
comb=计数器(comb)
ks=(组合中的v代表v唯一)
完成=错误
虽然(不麻烦)和(不完整):
尝试:
k=下一个(ks)
如果梳[k]>1:
如果原始数据[k]=k:
对于itertools中的i.组合(元素_唯一,n):
对于itertools中的j。带替换的组合(元素其他,k-n):
如果梳形检查(j,元件):