【问题标题】:Get list of combinations for K groups of N members and L groups of M members获取 K 组 N 成员和 L 组 M 成员的组合列表
【发布时间】:2017-08-11 22:35:17
【问题描述】:

在 Python 中;在给定可能成员g的列表的情况下,获取kn成员组和lm成员组组合列表的最佳方法是什么?

示例,给定一个元素列表:

g = ["A", "B", "C", "D", "E", "F", "G"]

我想要的是列出所有组合的li,例如2(=k) 组 2(=n) 和 1(=l) 组 3(=m):

[["AB", "CD", "EFG"],
 ["AC", "BD", "EFG"],
 ["AD", "CB", "EFG"],
 ["AE", "CD", "BFG"],
 ["AF", "CD", "BEG"],... ]
  1. 我不希望在任何组中重复任何元素(相当于说:我希望每个不同的元素在所有组中针对每个不同的组合出现一次)。

    例如["AB", "AD", "EFG"] 不是一个有效的组合,因为它在所有组中多次包含元素 A

  2. 我不希望组内有不同的排列

    例如["AB", "CD", "EFG"] 不应以["BA", "DC", "EGF"] 之类的形式重复。

  3. 另外,如果一个组合出现在任何k-groups 中,如果l-groups 相同(l-groups 相同),我不希望 k 组中出现相同的组合。

    例如如果["AB", "CD", "EFG"] 出现,[ "CD", "AB", "EFG"] 不应再次出现。

需要明确的是,我只对组总是整齐/完全适合要使用的整个元素组的情况感兴趣(g):

例如2x2 + 1x3 == 7 == len(["A", "B", "C", "D", "E", "F", "G"]), 1x2 + 1x3 == 5 == len(["A", "B", "C", "D", "E"]).


我可以使用 Python's permutations function 并在每个排列中将 k 分组到 nl 分组 m 中,但是对于更多元素,我会有很多不必要的迭代。

【问题讨论】:

  • 你能在一个字符串中复制元素吗?例如,拥有["AB", "BC", "EFE"] 是否合法?
  • 不。任何元素都不能在另一个子组中重复。
  • 修复您的帖子?您已经在某些示例中重复了 BC,例如 ["AB", "BC", "EFG"]
  • 鉴于您的澄清,我发布的答案是错误的:它会跨组发出重复项。这个问题并不像最初出现的那么简单:每个组都必须是词典中的组合,并且您需要组的所有互斥排列。跨度>
  • 你想要["AB", "CD", "EFG"]["CD", "AB", "EFG"] 还是只想要其中一个?

标签: python algorithm python-2.7 combinations


【解决方案1】:

编辑:已编辑代码以满足更新的要求(规则 3)。

代码:

import itertools as it


def unique_group(iterable, k, n):
    """Return an iterator, comprising groups of size `k` with combinations of size `n`."""
    # Build separate combinations of `n` characters
    groups = ("".join(i) for i in it.combinations(iterable, n))    # 'AB', 'AC', 'AD', ...

    # Build unique groups of `k` by keeping the longest sets of characters
    return (i for i in it.combinations(groups, k) 
                if len(set("".join(i))) == sum((map(len, i))))     # ('AB', 'CD'), ('AB', 'CE'), ... 


def combined(groups1, groups2):
    """Return an iterator with unique combinations of groups (k and l)."""
    # Build a unique cartesian product of groups `k` and `l`, filtering non-disjoints
    return (i[0] + i[1]
               for i in it.product(groups1, groups2) 
               if set("".join(i[0])).isdisjoint(set("".join(i[-1]))))


iterable = "ABCDEFG"
g1 = unique_group(iterable, 2, 2)
g2 = unique_group(iterable, 1, 3)
result = list(combined(g1, g2))
print(len(result))
result

输出:

105

[('AB', 'CD', 'EFG'),
 ('AB', 'CE', 'DFG'),
 ...,
 ('BC', 'AD', 'EFG'),
 ('BC', 'AE', 'DFG'),
 ...,
]

可以在demonstration 中找到详细信息和见解。

【讨论】:

  • 我想你快到了。尝试这个例如iterable = "ABCDE"g1 = unique_group(iterable, 1, 2)g2 = unique_group(iterable, 1, 3) 导致 [('AD', 'BCE'), ('AE', 'BCD'), ('BC', 'ADE'), ('BD', 'ACE'), ('BE', 'ACD'), ('CD', 'ABE'), ('CE', 'ABD'), ('DE', 'ABC')]。这是例如缺少('AB', 'CDE')('AC', 'BDE') 的组合。
  • 有了你的新输入,我得到了所有这些组合(10 个项目的列表)。
【解决方案2】:

编辑:澄清后,我添加了一行应该完成解决方案...

这个怎么样,它使用了几个itertoolsflatten recipe。无论如何,我认为 itertools.combinations 是您想要使用的:

from itertools import combinations, chain, product

def flatten(listOfLists):
    "Flatten one level of nesting"
    return chain.from_iterable(listOfLists)

lico = lambda li,x: list( combinations(li,x) ) 
def get_funky_groups( elements, k,n,l,m ):     
    kn = lico( lico(elements,n),k)  # k groups of n elements
    lm = lico( lico( elements,m), l)  # l groups of m elements
    results =  [map( lambda x: "".join(x), flatten(r)) for r in product(kn, lm)]
    # added this line so that only each element was used once.. 
    return [ r for r in results if len(set( flatten( r))) == len(g) ]

对于您的示例列表,这会产生 105 个结果

In [3]: g = ["A", "B", "C", "D", "E", "F", "G"]

In [4]: results = get_funky_groups(g, 2,2,1,3)

In [5]: results[:10]
Out[5]: 
[['AB', 'CD', 'EFG'],
 ['AB', 'CE', 'DFG'],
 ['AB', 'CF', 'DEG'],
 ['AB', 'CG', 'DEF'],
 ['AB', 'DE', 'CFG'],
 ['AB', 'DF', 'CEG'],
 ['AB', 'DG', 'CEF'],
 ['AB', 'EF', 'CDG'],
 ['AB', 'EG', 'CDF'],
 ['AB', 'FG', 'CDE']]

In [6]: len( results) 
Out[6]: 105

In [7]: g = ["A", "B", "C", "D", "E"]

In [8]: results = get_funky_groups(g, 1,2,1,3)

In [9]: results
Out[9]: 
[['AB', 'CDE'],
 ['AC', 'BDE'],
 ['AD', 'BCE'],
 ['AE', 'BCD'],
 ['BC', 'ADE'],
 ['BD', 'ACE'],
 ['BE', 'ACD'],
 ['CD', 'ABE'],
 ['CE', 'ABD'],
 ['DE', 'ABC']]

可能你不想要一个依赖于字符串元素的答案

def get_funky_groups_anyhashable( elements, k,n,l,m ):     
    kn = lico( lico(elements,n),k)  # k groups of n elements
    lm = lico( lico( elements,m), l)  # l groups of m elements
    results =  [ list(flatten(r)) for r in product(kn, lm)]
    # added this line so that only each element was used once.. 
    return [ r for r in results if len(set( flatten( r))) == len(g) ]

In [103]: g = ["A1", "B2", 232, "D0", 32]

In [104]: get_funky_groups_anyhashable(g, 1,2,1,3)
Out[104]: 
[[('A1', 'B2'), (232, 'D0', 32)],
 [('A1', 232), ('B2', 'D0', 32)],
 [('A1', 'D0'), ('B2', 232, 32)],
 [('A1', 32), ('B2', 232, 'D0')],
 [('B2', 232), ('A1', 'D0', 32)],
 [('B2', 'D0'), ('A1', 232, 32)],
 [('B2', 32), ('A1', 232, 'D0')],
 [(232, 'D0'), ('A1', 'B2', 32)],
 [(232, 32), ('A1', 'B2', 'D0')],
 [('D0', 32), ('A1', 'B2', 232)]]

另外值得注意的是,以防性能出现问题

In [132]: lico( combinations( g,2),1) == lico( lico( g,2),1 )
Out[132]: True

【讨论】:

  • 很抱歉,如果不清楚,但这不是我要找的。例如。您的第一次迭代['AB', 'AC', 'ABC']:每个元素只能拥有一次,因此您不能在任何组中拥有多个A'、多个B、多个C。 (我编辑了原始帖子;参见规则 #1)。
  • 嗯,我想你可以检查扁平化元素的集合,如我添加的简单编辑中所示。通过使用combinations 工具,您已经比使用链接到的permutations itertool 进行的迭代次数要少得多.. 但您可以进一步优化...
【解决方案3】:

这个问题并不像最初出现的那么简单:每个组都必须是词典中的组合,并且您需要组的所有互斥排列.

我认为这将需要您编写一个递归生成器,获取字母表和大小列表。类似于以下代码(恐怕我还没有测试过......):

def foo(lexicon, size_list, result):
    if len(size_list) == 0:
        yield result
        return
    size = size_list[0]
    for group in itertools.combinations(lexicon, size):
        # remove used items from the lexicon
        next_lex = lexicon[:]
        for item in group:
            next_lex.remove(item)
        # recur at next level
        foo(next_lex, size_list[1:], result + [group] )

foo( "ABCDEFG", [2, 2, 3], [] )

【讨论】:

  • 返回一个空的生成器。
猜你喜欢
  • 2016-03-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-13
相关资源
最近更新 更多