正如 cmets 中所讨论的,范围都是相同的,我们应该使用整数。那么这是一种很好的方式。
不是生成四个数字并测试它们加起来是否为 10,而是生成 三个 数字,将区间 [0, 10] 划分为四个区间。例如,当我们在 (3, 4, 8) 处有切割,并且我们添加了端点 0 和 10,那么我们就有了边界 (0, 3, 4, 8, 10)。相邻边界之间的差异为 (3-0, 4-3, 8-4, 10-8) = (3, 1, 4, 2)。那是四个数字加起来等于 10。下面是执行此操作的代码:
n = 10
import itertools, operator
for cuts in itertools.combinations_with_replacement(range(n+1), 3):
combi = list(map(operator.sub, cuts + (n,), (0,) + cuts))
if max(combi) < n:
print(combi)
打印出来的:
[0, 0, 1, 9]
[0, 0, 2, 8]
[0, 0, 3, 7]
[0, 0, 4, 6]
[0, 0, 5, 5]
[0, 0, 6, 4]
[0, 0, 7, 3]
[0, 0, 8, 2]
[0, 0, 9, 1]
[0, 1, 0, 9]
[0, 1, 1, 8]
[0, 1, 2, 7]
...
...
[7, 2, 0, 1]
[7, 2, 1, 0]
[7, 3, 0, 0]
[8, 0, 0, 2]
[8, 0, 1, 1]
[8, 0, 2, 0]
[8, 1, 0, 1]
[8, 1, 1, 0]
[8, 2, 0, 0]
[9, 0, 0, 1]
[9, 0, 1, 0]
[9, 1, 0, 0]
它非常有效,因为它可以非常直接地生成组合。 if max(combi) < n 只过滤掉[0, 0, 0, 10]、[0, 0, 10, 0]、[0, 10, 0, 0] 和[10, 0, 0, 0]。
这是您的原始设备、我的设备和 @Mijamo 之间的速度比较,范围为 100 个数字,例如您的示例:
drum: 21.027 seconds
Stefan: 0.708 seconds
Mijamo: 62.864 seconds
该测试的完整代码:
import itertools, operator
from timeit import timeit
def drum(n):
out = []
for a in range(n):
for b in range(n):
for c in range(n):
for d in range(n):
if a + b + c + d == n:
out.append((a, b, c, d))
return out
def Stefan(n):
combinations = (map(operator.sub, cuts + (n,), (0,) + cuts)
for cuts in itertools.combinations_with_replacement(range(n+1), 3))
return [c for c in combinations if max(c) < n]
def Mijamo(n):
combinations = itertools.product(range(n), repeat=4)
return [tuple for tuple in combinations if sum(tuple) == n]
for func in drum, Stefan, Mijamo:
print '%6s: %6.3f seconds' % (func.__name__, timeit(lambda: func(100), number=1))