python中L组K项中N个元素的组合

Posted

技术标签:

【中文标题】python中L组K项中N个元素的组合【英文标题】:Combination of N elements in L groups of K items in python 【发布时间】:2020-08-23 16:54:47 【问题描述】:

给定一个包含 N 个元素的列表,例如

mylist = [0, 1, 2, 3, 4, 5, 6, 7]

我想找到 L 组中 K 个元素的所有组合,例如对于K=4L=2, 结果是

          L=0            L=1 
 1)   [0, 1, 2, 3]   [4, 5, 6, 7]
 2)   [0, 1, 2, 4]   [3, 5, 6, 7]
 3)   [0, 1, 2, 5]   [3, 4, 6, 7]

              ... etc...
69)   [4, 5, 6, 7]   [0, 1, 2, 3]

请注意,[0, 1, 2, 3][0, 1, 3, 2] 将被视为第一组的相同组合。

对于L=2的情况,我使用如下

from itertools import combinations

N = 8
M = 4
L = N // M

combs = list(combinations(range(N), M))
allidx = list(range(N))

for c, comb in enumerate(combs):

    idx1 = list(comb)
    idx2 = list(set(allidx) - set(idx1))

    print(c, idx1,'\t',idx2)

首先,这种“组合”的数学定义是什么?

其次,在L>2 的情况下,有没有比计算所有排列并在之后修剪它们更有效的方法来计算它们?

【问题讨论】:

【参考方案1】:

您可以使用递归函数,从列表中获取 k 元素的所有组合,并将它们与剩余元素的组合组合。

import itertools

def combs(lst, k, l):
    if l == 0:
        yield []
    else:
        for c in itertools.combinations(lst, k):
            rest = [x for x in lst if x not in c]
            for res in combs(rest, k, l-1):
                yield [c, *res]

mylist = [0, 1, 2, 3, 4, 5, 6, 7]
for res in combs(mylist, 4, 2):
    print(res)

这里,rest = [x for x in lst if x not in c] 部分仅在列表中的元素是唯一的情况下才有效。如果可能有重复的元素,您可以改为只获取索引组合,例如像这样(其余保持不变):

        for idc in itertools.combinations(range(len(lst)), k):
            comb = [lst[i] for i in idc]
            idc_set = set(idc)
            rest = [x for i, x in enumerate(lst) if i not in idc_set]
            for res in combs(rest, k, l-1):
                yield [comb, *res]

(另外,这假设lst 至少有l*k 元素。)

【讨论】:

可能是错字? yield [comb, *rest] --> yield [comb, *res] @Chutlhu 哦,天哪,当然可以!在我的辩护中,在 k=2 的情况下,两者产生几乎相同的结果,只是缺少一对括号。【参考方案2】:

这是从长度为 n 的集合中生成所有大小为 k 的唯一分区的方法。

这样的分区数是(p是零件数,等于你的L):

NPK(n,k) = n! / ((k!)^p * p!)

并且增长迅速(n=9,k=3 时为 280)。

算法递归地将项目分布到各个部分。为了避免重复生成相同的分区(如01 23 4501 45 23),我们应该限制每个组的前导(最小)元素的位置。

这里我使用lastfilled参数作为最右边填充部分的索引,所以第0项总是属于第0部分,第1项可能属于第0部分或第1部分,但不属于第2部分,依此类推。有了中间结果01 __ __,我们只能在下一级制作01 2_ __,而不是01 __ 2_

def genp(l, parts, cnts, n, k, p,  m, lastfilled):
    if m == n:
        l.append(parts[:])
        return
    for i in range(min(p, lastfilled + 2)):
        if cnts[i] < k:
            parts[i][cnts[i]] = m
            cnts[i] += 1
            genp(l, parts, cnts, n, k, p, m+1, max(i, lastfilled))
            cnts[i] -= 1


def genpartssizek(n, k):
    l = []
    p = n // k
    parts = [[0]*k for _ in range(p)]
    cnts = [0]*p
    genp(l, parts, cnts, n, k, p,  0, -1)
    return l

print(genpartssizek(6,2))

[[[0, 1], [2, 3], [4, 5]], 
 [[0, 1], [2, 4], [3, 5]], 
 [[0, 1], [2, 5], [3, 4]],
 [[0, 2], [1, 3], [4, 5]], 
 [[0, 2], [1, 4], [3, 5]], 
 [[0, 2], [1, 5], [3, 4]], 
 [[0, 3], [1, 2], [4, 5]], 
 [[0, 4], [1, 2], [3, 5]], 
 [[0, 5], [1, 2], [3, 4]], 
 [[0, 3], [1, 4], [2, 5]], 
 [[0, 3], [1, 5], [2, 4]], 
 [[0, 4], [1, 3], [2, 5]], 
 [[0, 5], [1, 3], [2, 4]], 
 [[0, 4], [1, 5], [2, 3]], 
 [[0, 5], [1, 4], [2, 3]]]

【讨论】:

以上是关于python中L组K项中N个元素的组合的主要内容,如果未能解决你的问题,请参考以下文章

合并k个有序数组

在N个元素的数组中获取K个元素的所有组合问题

从 n 返回 k 个元素的连续组合的按需算法

从 K 组计算 N 条路径的算法

java题目好难啊?求大侠解答,谢谢、、 设计算法求解从集合1...n中选取k(k<=n)个元素的所有组合。

HDU 5884 (贪心)