具有3个正整数的数组,其总和为n

时间:2019-06-21 15:42:26

标签: python

我使用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循环,尽管每次我得到重复的组合时。 预先感谢!

4 个答案:

答案 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)