【问题标题】:combinations of elements of different tuples in the list列表中不同元组的元素组合
【发布时间】:2014-04-02 09:38:12
【问题描述】:

我有一个像这样的元组列表:[(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] 等),但我考虑过在之后排序和/或消除重复项创建整个列表。

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

【问题讨论】:

  • itertools 中的一些方法是你最好的选择。
  • 我正在查看它们,但老实说并没有发现任何真正有用的东西(至少直接解决了我的问题;-))。我正在考虑将所有元组元素加入一个长列表,然后从它们中进行所有组合,并最终从每个元组中消除那些不包含至少一个元素的组合。但我不确定这是最明智的方法。

标签: python tuples combinatorics


【解决方案1】:

您想要 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]]

【讨论】:

    【解决方案2】:

    让我们用XY表示这两个列表,用|X||Y|表示它们的长度。

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

    所以两个幂集的乘积长度为2^(|X|+|Y|)。 对于这个产品中的每个项目,我们需要组合部件,获取集合(从部件中删除重复项)以形成一个新的集合。然后我们需要获取该集合的集合以从集合中删除重复项。必须获取完整集合的集合可能会占用大量内存,因为它需要一次将完整集合保存在内存中。

    但是,我认为有一种更快的方法可以达到预期的最终结果。如果将XY 组合成一个集合XY,则XY 的幂集长度为2^(|XY|)。如果XY 共享任何共同项,则此长度小于2^(|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)]
    

    【讨论】:

      猜你喜欢
      • 2016-12-19
      • 2023-02-19
      • 1970-01-01
      • 2016-08-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-06-15
      • 1970-01-01
      相关资源
      最近更新 更多