这是我解决它的方法,使用递归函数查找总和为给定值的所有组合:
def ordered_combinations(pop, n):
pop = sorted(pop)
for s in range(sum(pop[:n]), sum(pop[-n:])+1):
yield from get_sums(pop, s, n)
def get_sums(pop, s, n):
if n == 1:
if s in pop:
yield [s]
return
for i, v in enumerate(pop):
if sum(pop[i:i+n]) > s:
return
for rest in get_sums(pop[i+1:], s-v, n-1):
rest.append(v)
yield rest
这是它的输出示例:
>>> for c in ordered_combinations(range(1, 8), 4):
print(c, sum(c))
[4, 3, 2, 1] 10
[5, 3, 2, 1] 11
[6, 3, 2, 1] 12
[5, 4, 2, 1] 12
[7, 3, 2, 1] 13
[6, 4, 2, 1] 13
[5, 4, 3, 1] 13
[7, 4, 2, 1] 14
[6, 5, 2, 1] 14
[6, 4, 3, 1] 14
[5, 4, 3, 2] 14
[7, 5, 2, 1] 15
[7, 4, 3, 1] 15
[6, 5, 3, 1] 15
[6, 4, 3, 2] 15
[7, 6, 2, 1] 16
[7, 5, 3, 1] 16
[6, 5, 4, 1] 16
[7, 4, 3, 2] 16
[6, 5, 3, 2] 16
[7, 6, 3, 1] 17
[7, 5, 4, 1] 17
[7, 5, 3, 2] 17
[6, 5, 4, 2] 17
[7, 6, 4, 1] 18
[7, 6, 3, 2] 18
[7, 5, 4, 2] 18
[6, 5, 4, 3] 18
[7, 6, 5, 1] 19
[7, 6, 4, 2] 19
[7, 5, 4, 3] 19
[7, 6, 5, 2] 20
[7, 6, 4, 3] 20
[7, 6, 5, 3] 21
[7, 6, 5, 4] 22
组合总是首先产生最大的值,作为我如何将它们构建为列表的工件(通过在末尾附加小值,而不是通过连接到前面)。如果您希望它们从小到大排序,您可以将这些rest.append(v); yield rest
行更改为yield [v]+rest
.
该代码使用yield from
Python 3.3 引入的语法。如果您使用的是不支持该功能的早期版本,则可以使用以下等效代码:
for v in get_sums(pop, s, n):
yield v
该代码甚至可以处理您描述的从 800 个成员范围中提取的 400 个组合的极端情况。这是该计算的前 20 个结果(仅显示最大的 10 个值,因为其余的都是相同的 390 到 1),以及它们的总和:
>>> for i, v in enumerate(ordered_combinations(range(1, 800), 400)):
if i >= 20:
break
print(v[:10], sum(v))
[400, 399, 398, 397, 396, 395, 394, 393, 392, 391] 80200
[401, 399, 398, 397, 396, 395, 394, 393, 392, 391] 80201
[402, 399, 398, 397, 396, 395, 394, 393, 392, 391] 80202
[401, 400, 398, 397, 396, 395, 394, 393, 392, 391] 80202
[403, 399, 398, 397, 396, 395, 394, 393, 392, 391] 80203
[402, 400, 398, 397, 396, 395, 394, 393, 392, 391] 80203
[401, 400, 399, 397, 396, 395, 394, 393, 392, 391] 80203
[404, 399, 398, 397, 396, 395, 394, 393, 392, 391] 80204
[403, 400, 398, 397, 396, 395, 394, 393, 392, 391] 80204
[402, 401, 398, 397, 396, 395, 394, 393, 392, 391] 80204
[402, 400, 399, 397, 396, 395, 394, 393, 392, 391] 80204
[401, 400, 399, 398, 396, 395, 394, 393, 392, 391] 80204
[405, 399, 398, 397, 396, 395, 394, 393, 392, 391] 80205
[404, 400, 398, 397, 396, 395, 394, 393, 392, 391] 80205
[403, 401, 398, 397, 396, 395, 394, 393, 392, 391] 80205
[403, 400, 399, 397, 396, 395, 394, 393, 392, 391] 80205
[402, 401, 399, 397, 396, 395, 394, 393, 392, 391] 80205
[402, 400, 399, 398, 396, 395, 394, 393, 392, 391] 80205
[401, 400, 399, 398, 397, 395, 394, 393, 392, 391] 80205
[406, 399, 398, 397, 396, 395, 394, 393, 392, 391] 80206
因为它是递归的,如果您请求 1000 个组合,此代码可能会失败(这是由于 Python 的默认递归限制)。sys.setrecursionlimit
如有必要,您可以修改它的限制。
如果你对一个非常大的人口进行非常深入的研究,它也可能存在内存问题,因为get_sums
在递归步骤中对人口进行切片(并因此复制)。如果您对该代码的使用仅使用range
s,您可能可以通过从 中删除该pop = sorted(pop)
行来解决内存问题ordered_combinations
,因为 Python 3 的range
对象可以有效地切片(即range(1,100)[10:]
is range(11,100)
)。