列表中不同元组的元素组合

时间:2014-04-01 23:48:51

标签: python tuples combinatorics

我有一个像这样的元组列表:[(1, 2, 3), (2, 4)](列表的长度,元组的长度可能会有所不同),我希望得到包含至少一个元素的所有组合列表中的每个元组,以及包含更多元素的那些组合。

所以结果应该在这个例子中:
[[1, 2, 3, 2, 4], [1, 2, 2, 4], [2, 3, 2, 4], [1, 2, 4], [2, 2, 4], [3, 2, 4], [1, 2, 3, 2], [1, 2, 3, 4], [1, 2, 2], [1, 2, 4], [2, 3, 2], [2, 3, 4], [1, 2], [1, 4], [2, 2], [2, 4], [3, 2], [3, 4]]

最小的结果应包含等于原始列表中元组数的元素数,最大的元素应包含元组中存在的所有元素。

元素的顺序无关紧要,应该最终消除重复(所以[1, 2, 3, 2, 4] = [1, 2, 3, 4]并且应该只在结果中一次,类似[3, 2] = [2, 3]等),但我想到了排序和/或者在创建整个列表后删除重复项。

最好的方法是什么?坦率地说,我甚至不知道如何正确地开始......

2 个答案:

答案 0 :(得分:4)

你想要L中物品的笛卡尔积 - 除非它们中的任何一个是空的。一种方法是在构建powerset时将空元素留下来。

from itertools import product, combinations, chain
L = [(1, 2, 3), (2, 4)]
def powerset(iterable):
    "powerset minus the empty element"
    s = list(iterable)
    return chain.from_iterable(combinations(s, r) for r in range(1, len(s)+1))

print [list(chain.from_iterable(c)) for c in product(*(powerset(x) for x in L))]

打印

[[1, 2], [1, 4], [1, 2, 4], [2, 2], [2, 4], [2, 2, 4], [3, 2], [3, 4], [3, 2, 4], [1, 2, 2], [1, 2, 4], [1, 2, 2, 4], [1, 3, 2], [1, 3, 4], [1, 3, 2, 4], [2, 3, 2], [2, 3, 4], [2, 3, 2, 4], [1, 2, 3, 2], [1, 2, 3, 4], [1, 2, 3, 2, 4]]

答案 1 :(得分:1)

我们将这两个列表用XY表示,其长度用|X||Y|表示。

powerset(X)的长度为2^|X|,而powerset(Y)的长度为2^|Y|

因此两个powersets的产品长度为2^(|X|+|Y|)。 对于本产品中的每个项目,我们需要组合这些部分,取出集合(从部件中删除重复部分)以形成新的集合。然后我们需要使用此集合的集合来删除集合中的重复项。必须采用完整集合的集合可能是内存密集型的,因为它需要立即将完整集合保存在内存中。

但是,我认为有更快的方法来达到理想的最终结果。如果您将XY合并到一个集XY中,那么XY的powerset的长度为2^(|XY|)。如果2^(|X|+|Y|)X共享任何共同项,则此长度小于Y。因此,您可以为每个项目共同保存2倍。

对于这个powerset中的每个项目,我们只需要检查X中的元素和Y中的元素。收集所有这些项目,我们就完成了。这样做的工作少得多,并且内存密集程度较低,因为结果可以由迭代器生成。


import itertools as IT

def powerset(iterable, reverse=False, rvals=None):
    """powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"""
    s = list(iterable)
    N = len(s)
    if rvals is None:
        rvals = range(N, -1, -1) if reverse else range(N + 1)
    return IT.chain.from_iterable(
        IT.combinations(s, r) for r in rvals)

def powerreps(X, Y):
    """
    Return powerset with at least one representative from X and Y
    """
    XY = set(X).union(Y)
    for rep in powerset(XY, rvals=range(2, len(XY))):
        if any(x in rep for x in X) and any(y in rep for y in Y):
            yield rep

X, Y = (1, 2, 3), (2, 4)
print(list(powerreps(X, Y)))

产量

[(1, 2), (1, 4), (2, 3), (2, 4), (3, 4), (1, 2, 3), (1, 2, 4), (1, 3, 4), (2, 3, 4), (1, 2, 3, 4)]