我正在寻找一种算法和/或 Python 代码来生成将一组n
元素划分为零组或多组r
元素和余数的所有可能方式。例如,给定一个集合:
[1,2,3,4,5]
和n = 5
,r = 2
我想得到
((1,2,3,4,5),)
((1,2),(3,4,5))
((1,3),(2,4,5))
...
((1,2),(3,4),(5,))
((1,2),(3,5),(4,))
...
换句话说,从集合中提取0组两个项目的结果,加上从集合中提取1组两个项目的结果,再加上从集合中提取2组两个项目的结果,...如果n
更大,这将继续。
生成这些结果的顺序并不重要,每个单独组中元素的顺序也不重要,结果中组的顺序也不重要。(例如((1,3),(2,4,5))
,等价于((3,1),(4,5,2))
和((2,5,4),(1,3))
等。)我正在寻找的是每个不同的结果至少产生一次,最好是一次,以尽可能有效的方式。
蛮力方法是r
从n
元素中生成所有可能的组合,然后创建任意数量的这些组合(powerset)的所有可能组,迭代它们并仅处理组中的组合在其中没有元素的组合常见的。即使是少量元素也需要很长时间(它需要迭代 2^(n!/r!(nr)!) 个组,因此复杂度是双指数的)。
基于这个问题中给出的代码,这基本上是特殊情况r = 2
,n
甚至,我想出了以下内容:
def distinct_combination_groups(iterable, r):
tpl = tuple(iterable)
yield (tpl,)
if len(tpl) > r:
for c in combinations(tpl, r):
for g in distinct_combination_groups(set(tpl) - set(c), r):
yield ((c,) + g)
这似乎确实产生了所有可能的结果,但它包括一些重复,当它们n
相当大时,它们中的一个不平凡的数量。所以我希望有一种算法可以避免重复。