【问题标题】:List all combinations but with restriction列出所有组合但有限制
【发布时间】:2018-01-04 11:43:46
【问题描述】:

我找到了列出所有可能的 n 元素组合的方法,这些元素组合在 k 个元素的集合中。从数学上看,数字很简单:n!/(k!* (n-k)!) 并且 python 代码使用 itertools 非常简单:

>>> import itertools
>>> a = [1,2,3,4]
>>> list(itertools.combinations(a,3))
[(1, 2, 3), (1, 2, 4), (1, 3, 4), (2, 3, 4)]

但是如何实现这样的限制:只列出只有 m 个共同元素的组?所以在前面的例子中,对于 m=1,结果应该是:

[(1, 2, 3)]

有 5 个元素且 m=1:

>>> b=[1,2,3,4,5]
>>> list(itertools.combinations(b,3))
[(1, 2, 3), (1, 2, 4), (1, 2, 5), (1, 3, 4), (1, 3, 5), (1, 4, 5), (2, 3, 4), (2, 3, 5), (2, 4, 5), (3, 4, 5)]

所以我的结果是:

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

这个问题的实际应用是如何对人进行分组,考虑到只有 m 人可以在结果组中重复。想法是找到有不同人的群体,避免成群结队的“朋友”。想象一下这样的学校活动,重复了几天,我们希望确保尽可能避免人们与其他人重复。

【问题讨论】:

  • 示例中的所有元组至少有两个不同的元素。
  • 抱歉,我实在忍不住了,@KlausD。
  • @joanba 不同的元素相对于什么?第一个元组?
  • 你改写了,但没有更清楚。
  • 你回答得这么快,只是编辑帖子更容易理解

标签: python combinations itertools


【解决方案1】:

可以使用for循环将第一个组合与其余组合进行比较(使用集合交集函数"&"):

def getsets(a, n):              # FUNCTION FOR THIS PURPOSE
    combos = list(itertools.combinations(a,n))  # GET ALL COMBINATIONS
    print(combos[0])            # PRINT FIRST COMBINATION
    for i in combos[1:]:        # FOR EACH OF REST
        if len(set(combos[0]) & set(i)) == 1:       # IF ONLY 1 ITEM COMMON WITH INITIAL
            print(i)            # PRINT IT

测试:

getsets([1,2,3,4], 3)
print("---------")
getsets([1,2,3,4,5], 3)

输出:

(1, 2, 3)   # INITIAL SET ONLY
---------
(1, 2, 3)   # INITIAL SET
(1, 4, 5)   # ONLY '1' IS COMMON WITH FIRST SET
(2, 4, 5)   # ONLY '2' IS COMMON WITH FIRST SET
(3, 4, 5)   # ONLY '3' IS COMMON WITH FIRST SET
{1, 2, 3}   # ONLY '4' IS COMMON WITH FIRST SET

【讨论】:

    【解决方案2】:

    你可以使用for循环:

    m = 1
    combos = list(itertools.combinations(a,3))
    result = []
    for combo in combos:
        if result:
            if all([sum([1 for item in combo if item in tup]) == 1 for tup in result]):
                result.append(combo)
        else:
            result.append(combo)
    result
    #[(1, 2, 3), (1, 4, 5)]
    

    这样您就可以控制共同成员的数量并获得所需的输出。

    【讨论】:

    • @erhesto 完全正确,但我为变量写了新名称。
    【解决方案3】:

    看起来,你得到的结果的逻辑等价物是这样的:

    a = range(1,6)
    shared_elem_count = 1
    group_count = 3
    
    combinations = itertools.combinations(a, group_count)
    results = []
    
    for combination in combinations:
        res_union = set().union(*results)
        intersection = set(combination).intersection(res_union)
        if len(intersection) <= shared_elem_count:
            results.append(combination)
    

    在您的示例元组中,与任何其他可用元素共享的公共元素不超过 m。但正如它在 cmets 中所指出的那样,该解决方案严格遵循操作顺序。第一个元组确定下一个元组是可以接受的。

    请注意,您在评论中所说的是一个更广泛的问题:将 n 人分组到 k 数量不超过 mpeople 的组中会显着比我们“没有过滤掉”的解决方案更多。例如,您指出对于m = 1a = (1,2,3,4,5) 的解决方案是:

    (1,2,3),(1,4,5)
    

    ...但实际上还有更多的解决方案,例如:

    (2,3,4),(1,2,5)
    (3,4,5),(1,2,3)
    (1,4,5),(2,3,4)
    

    所以我不完全确定过滤组合是否是解决您的问题的正确方法(尽管如此, 您实际上想要实现什么并不是很清楚,所以谁知道)。

    【讨论】:

    • (2,3,4) 不可能是一个解决方案,因为它与初始解决方案 (1,2,3) 有 2 个共同元素
    • 你从未说过 what 是初始解决方案。如果您以不同的方式订购itertools.combinations(a, group_count),您可能会得到不同的“初始”解决方案”。我在答案中列出的实际上是解决方案的“组”(每个组有 2 个元素)而不是(1,2,3),(1,4,5) 的其他组合.
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多