简体   繁体   中英

How can I make Python code more efficient?

Newbie here.I have this code, that displays all possible sums to reach a certain number. But its complexity is too high and it takes too longe when numbers are too high. How can I refactor this to something more simple?

import itertools

def combos(n):

    result = []
    for i in range(n,0,-1):
        for seq in itertools.combinations_with_replacement(range(1,n+1), i):
            if sum(seq) == n:
                seq = list(seq)
                result.append(seq)
    return(result)

combos(4)

output:

[[1,1,1,1],[1,1,2],[1,3],[2,2],[4]]

The recursive version could be like this:

def combinations_max_sum(sum_max, i_max=None):

    if sum_max == 0:
        return [()]

    if not i_max or i_max > sum_max:
        i_max = sum_max

    combinations = [(i, *right_part)
                    for i in range(i_max, 0, -1)
                    for right_part in combinations_max_sum(sum_max-i, i_max=i)]

    return combinations

Test:

print(combinations_max_sum(4)) # [(4,), (3, 1), (2, 2), (2, 1, 1), (1, 1, 1, 1)]
print(combinations_max_sum(4, i_max=1)) # [(1, 1, 1, 1)]
print(combinations_max_sum(5))
# [(5,), (4, 1), (3, 2), (3, 1, 1), (2, 2, 1), (2, 1, 1, 1), (1, 1, 1, 1, 1)]

The idea to decompose the problem: a set of combinations can be written as a number concatenated with all the combinations for which the sum is n minus that first number.

A simpler code without dealing with repetition could be like this:

def combinations_with_repetition(n):

    if n == 0:
        return [()]

    combinations = [(i, *right_part)  # concatenation
                    for i in range(1, n+1)  # for all possible first number 
                    for right_part in combinations_with_repetition(n-i)]
                    # ^ all possible combinations
                    #   given the first number i

    return combinations

which gives:

combinations_with_repetition(3)
# [(1, 2), (1, 1, 1), (2, 1), (3,)]

(1, 2) and (2, 1) are similar, to prevent this a i_max argument is added (see the first function). The idea here is to go always in descending order. A number on the right is always equal or smaller than a number on the left. This maximum number is passed as an argument, and the loop start with it instead of the total sum asked.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM