与我的 cmets 建议的不同,我无法快速找到基于 itertools 的相对快速的解决方案!编辑:这不再是真的,我有一个相当短(但缓慢且不可读)的解决方案,主要使用 itertools,请参阅答案的结尾。这就是我得到的:
我们的想法是,我们找到加起来等于列表长度的所有整数组合,然后得到具有该长度切片的列表。
例如对于长度为 3 的列表,组合或分区是 (3)、(2, 1)、(1, 2) 和 (1, 1, 1)。所以我们返回列表的前 3 项;前 2 个,然后是下一个 1;第一个,然后是下一个 2,第一个,然后是下一个,然后是下一个。
我从here 获得了整数分区代码。但是,分区函数不会返回分区的所有排列(即,对于 3,它只会返回 (3)、(2, 1) 和 (1, 1, 1)。所以我们需要在每个分区上调用 itertools.permutations分区。然后我们需要删除重复项 - 就像 permutations([1, 2, 3]) 是 [[1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]];permutations([1, 1, 1]) 是 [[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]]。删除重复项的简单方法是将每个元组列表转换为 set。
然后剩下的就是获取列表的切片以获取元组中的长度。
例如。 f([1, 2, 3], [0, 0, 1, 2, 1, 0]) 转到 [[0], [0, 1], [2, 1, 0]]。
我的定义是这样的:
def slice_by_lengths(lengths, the_list):
for length in lengths:
new = []
for i in range(length):
new.append(the_list.pop(0))
yield new
现在我们将所有内容组合起来:
def subgrups(my_list):
partitions = partition(len(my_list))
permed = []
for each_partition in partitions:
permed.append(set(itertools.permutations(each_partition, len(each_partition))))
for each_tuple in itertools.chain(*permed):
yield list(slice_by_lengths(each_tuple, deepcopy(my_list)))
>>> for i in subgrups(my_list):
print(i)
[[1], [2], [3]]
[[1], [2, 3]]
[[1, 2], [3]]
[[1, 2, 3]]
此外,您还需要在程序顶部执行import itertools 和from copy import deepcopy。
编辑:您给定的输出不清楚。我以为您想要我给您的功能,但您的输出还包含[[1,3],[2]],其中输出中的元素的顺序不同,与您建议的输出的其余部分不同(我冒昧地假设您实际上是想要[[1, 2], [3]] 而不是[[1, 2], 3])。
也就是说,我假设你想要作为输出给出的内容是这样的:
[[1], [2], [3]]
[[1], [2, 3]]
[[1, 2], [3]]
[[1, 2, 3]]
如果实际上是这样的:
[[1], [2], [3]]
[[1], [2, 3]]
[[1, 2], [3]]
[[1, 2, 3]]
[[1], [3], [2]]
[[1], [3, 2]]
[[1, 3], [2]]
[[1, 3, 2]]
[[2], [1], [3]]
[[2], [1, 3]]
[[2, 1], [3]]
[[2, 1, 3]]
[[2], [3], [1]]
[[2], [3, 1]]
[[2, 3], [1]]
[[2, 3, 1]]
[[3], [1], [2]]
[[3], [1, 2]]
[[3, 1], [2]]
[[3, 1, 2]]
[[3], [2], [1]]
[[3], [2, 1]]
[[3, 2], [1]]
[[3, 2, 1]]
然后,您只需为原始列表的每个 3 长度排列调用 subgrups,例如对于itertools.permutations(my_list, len(my_list)) 中的每个排列。
编辑:现在兑现我对基于itertools 的简短解决方案的承诺。警告 - 它可能既不可读又慢。
首先我们用这个替换slice_by_lengths:
def sbl(lengths, the_list):
for index, length in enumerate(lengths):
total_so_far = sum(lengths[:index])
yield the_list[total_so_far:total_so_far+length]
然后从this的答案我们得到我们的整数分区函数:
def partition(number):
return {(x,) + y for x in range(1, number) for y in partition(number-x)} | {(number,)}
这个函数实际上为我们获取了整数分区的所有排列,所以我们不需要
for each_partition in partitions:
permed.append(set(itertools.permutations(each_partition, len(each_partition))))
了。但是,它比我们之前的要慢得多,因为它是递归的(我们正在 Python 中实现它)。
然后我们把它放在一起:
def subgrups(my_list):
for each_tuple in partition(len(my_list)):
yield list(slice_by_lengths(each_tuple, deepcopy(my_list)))
或可读性较差,但没有函数定义:
def subgrups(my_list):
for each_tuple in (lambda p, f=lambda n, g:
{(x,) + y for x in range(1, n) for y in g(n-x, g)} | {(n,)}:
f(p, f))(len(my_list)):
yield list(my_list[sum(each_tuple[:index]):sum(each_tuple[:index])+length] for index, length in enumerate(each_tuple))
这是一个函数定义和两行,与我最初所说的非常接近(尽管可读性差得多,速度也慢得多)!
(函数称为subgrups,因为该问题最初要求查找“所有子组”)