如何获得将长度为n的列表划分为m个子列表的所有可能组合

How to get all possible combinations of dividing a list of length n into m sublists

在我的具体情况下,我想获得将长度为 20 的列表划分为 4 个子列表的所有可能组合。子列表的长度可以从 1 到 17。因此,我想要一个包含所有可能组合的子列表列表。

开头列表:

list_init = [a, b,  c,  d,  e,  f,  g,  h,  i,  j,  k,  l,  m,  n,  o,  p,  q,  r,  s,  t]

包含所有组合的列表列表列表:

list_combs = [
[[a],   [b],    [c],    [d, e,  f,  g,  h,  i,  j,  k,  l,  m,  n,  o,  p,  q,  r,  s,  t]],
[[a,d], [b],    [c],    [e, f,  g,  h,  i,  j,  k,  l,  m,  n,  o,  p,  q,  r,  s,  t]],
[[a,d,e],   [b],    [c],    [f, g,  h,  i,  j,  k,  l,  m,  n,  o,  p,  q,  r,  s,  t]],
.
.
.
]

您可以通过将长度为 1、2、3...

from itertools import combinations
def partCombo(L,N=4):
    if N==1: yield [L]; return
    for size in range(1,len(L)-N+2):
        for combo in combinations(range(len(L)),size): # index combinations
            part      = list(L[i] for i in combo)      # first part
            remaining = list(L)
            for i in reversed(combo): del remaining[i] # unused items
            yield from ([part]+rest for rest in partCombo(remaining,N-1))

输出:

aList = list("abcdefg")
for part in partCombo(aList):
    print(part)

[['a'], ['b'], ['c'], ['d', 'e', 'f', 'g']]
[['a'], ['b'], ['d'], ['c', 'e', 'f', 'g']]
[['a'], ['b'], ['e'], ['c', 'd', 'f', 'g']]
[['a'], ['b'], ['f'], ['c', 'd', 'e', 'g']]
[['a'], ['b'], ['g'], ['c', 'd', 'e', 'f']]
[['a'], ['b'], ['c', 'd'], ['e', 'f', 'g']]
[['a'], ['b'], ['c', 'e'], ['d', 'f', 'g']]
[['a'], ['b'], ['c', 'f'], ['d', 'e', 'g']]
[['a'], ['b'], ['c', 'g'], ['d', 'e', 'f']]
[['a'], ['b'], ['d', 'e'], ['c', 'f', 'g']]
[['a'], ['b'], ['d', 'f'], ['c', 'e', 'g']]
[['a'], ['b'], ['d', 'g'], ['c', 'e', 'f']]
[['a'], ['b'], ['e', 'f'], ['c', 'd', 'g']]
[['a'], ['b'], ['e', 'g'], ['c', 'd', 'f']]
[['a'], ['b'], ['f', 'g'], ['c', 'd', 'e']]
[['a'], ['b'], ['c', 'd', 'e'], ['f', 'g']]
... and many more ... (total 8400)

对于包含 20 个项目的列表,将有 1,085,570,781,624 种组合。

from math import factorial
def countCombos(L,N=4):
    if N==1: return 1
    result = 0
    for size in range(1,L-N+2):
        c = factorial(L)//factorial(size)//factorial(L-size)
        c *= countCombos(L-size,N-1)
        result += c
    return result

sum(1 for _ in partCombo("abcdefg"))    # 8400
sum(1 for _ in partCombo("abcdefghij")) # 818520

countCombos(7)  # 8400 
countCombos(10) # 818520
countCombos(15) # 1016542800
countCombos(20) # 1085570781624

有那么多组合(20 项),将不可能输出所有组合的列表(它永远不会适合内存)。这就是函数被写成生成器的原因。尽管如此,在包含 20 个项目的列表中遍历生成器函数生成的所有组合仍需要很长时间(数周)。