我使用itertools
combinations_with_replacement
创建了一个生成器,该生成器返回3个正整数的总和,这些总和等于n:
def combinations(n):
for combo in combinations_with_replacement([i for i in range(1,n+1)],3):
if sum(combo) == n:
yield(combo)
例如combinations(7)
返回(1, 1, 5) (1, 2, 4) (1, 3, 3) (2, 2, 3)
不幸的是,随着n的增大,这个速度很快变得非常慢。是否有其他更有效的方法?我尝试过使用for循环,尽管每次我得到重复的组合时。
预先感谢!
答案 0 :(得分:4)
您不必获取所有三个数字的组合。您可以只得到两个个数字的组合,并且知道第三个数字是什么。
>>> n = 100
>>> combs_of_three = [(a,b,c) for (a,b,c) in combinations_with_replacement(range(1, n+1), 3) if a+b+c == n]
>>> combs_of_two = [(a,b,n-a-b) for (a,b) in combinations_with_replacement(range(1, n+1), 2) if n-a-b >= b]
>>> combs_of_three == combs_of_two
True
这快得多:
>>> %timeit [(a,b,c) for (a,b,c) in combinations_with_replacement(range(1, n+1), 3) if a+b+c == n]
9.97 ms ± 97.9 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
>>> %timeit [(a,b,n-a-b) for (a,b) in combinations_with_replacement(range(1, n+1), 2) if n-a-b >= b]
359 µs ± 2.06 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
答案 1 :(得分:1)
您可以使用递归函数进行此操作:选择一个适合的数字;然后重新计算剩余的大小和总数。
import math
def partition(total, size=3, lowest=1):
if size == 1:
return [[total]]
else:
result = []
# At each choice, pick no less than a "fair" proportion of the remaining total.
# This avoids duplicating combinations.
limit = math.ceil(total / size)
# Iterate `i` through the range [ limit, total-(size-1) ], inclusive
for i in range(limit, total-size+2):
for shorter in partition(total-i, size-1):
result.append(shorter + [i])
return result
print(partition( 7, 3))
print(partition(12, 3))
输出:
[[2, 2, 3], [1, 3, 3], [1, 2, 4], [1, 1, 5]]
[[4, 4, 4], [3, 5, 4], [2, 6, 4], [1, 7, 4], [3, 4, 5], [2, 5, 5], [1, 6, 5], [3, 3, 6], [2, 4, 6], [1, 5, 6], [2, 3, 7], [1, 4, 7], [2, 2, 8], [1, 3, 8], [1, 2, 9], [1, 1, 10]]
答案 2 :(得分:0)
简而言之,不,这是一个非常复杂的算法,很快就会总结为O(n ^ 3)
“算法”本身可能不会比O(n ^ 2)
更有效,但是您可以很容易地将其更改为O(n ^ 2)
。
def combinations(n):
for i in range(n - 2): # go up to n and add 1 + 1, assuming you don't want 0 and 0
for j in range(n - 2): # the same again.
if i + j >= n:
continue
yield (i, j, n - i - j) # there are probably more than just these, keep that in mind.
希望这至少会有所帮助。
答案 3 :(得分:0)
使用itertools.filterfalse
进行的另一项优化:
In [383]: def combinations_1(n):
...: for combo in combinations_with_replacement([i for i in range(1, n+1)], 3):
...: if sum(combo) == n:
...: yield(combo)
...:
...:
...:
In [384]: def combinations_2(n):
...: return itertools.filterfalse(lambda x: x[0]+x[1]+x[2] == n, \
...: combinations_with_replacement(range(1,n+1), 3))
...:
In [385]: %timeit list(combinations_1(17))
5.9 ms ± 322 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In [386]: %timeit list(combinations_2(17))
289 µs ± 28.3 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)