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=4
,L=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 45
和01 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个元素的组合的主要内容,如果未能解决你的问题,请参考以下文章