Get all numbers that add up to a number

Question:

I’m trying to find a way to display all the possible sets of X integers that add up to a given integer. for example to get all 2 integer sets that make 5 I would have:

1, 4
2, 3

Or for 3 integers that make 6:

1, 1, 4
1, 2, 3
2, 2, 2

I only need whole numbers not including 0 and it only needs to work on up to about 15 in a set and 30 max. number.

I’m not even sure if this has a term mathematically. It’s similar to factorisation I guess?

Asked By: Sam Machin

||

Answers:

There’s a snippet here:

from itertools import combinations, chain

def sum_to_n(n):
    'Generate the series of +ve integer lists which sum to a +ve integer, n.'
    from operator import sub
    b, mid, e = [0], list(range(1, n)), [n]
    splits = (d for i in range(n) for d in combinations(mid, i)) 
    return (list(map(sub, chain(s, e), chain(b, s))) for s in splits)

Use it like this:

for p in sum_to_n(4):    
    print p

Outputs:

[4]
[1, 3]
[2, 2]
[3, 1]
[1, 1, 2]
[1, 2, 1]
[2, 1, 1]
[1, 1, 1, 1]
Answered By: jbochi

These are called partitions of the integer in question. Others have provided links to define them.

A trick to do these things is often to do them recursively. For example, suppose I wanted to form all distinct ways to build 10 as the sum of exactly three integers, none of which appears more than once.

Look at the largest possible component in that sum. Can it be 10? No, since if the largest component is a 10, then what remains? I.e., 10 – 10 = 0. It turns out that if the largest element in the sum is a 7, then what remains, to be partitioned into a sum of two positive integers is 3. And we can break 3 into a sum of two distinct integers in exactly one way. So {7,2,1} is such a partition, and the only partition that involves an element as large as 7.

Can 6 be used as the largest element? If so, then we would have 4 remaining. And we can break 4 in exactly one way, to yield the partition {6,3,1}. Further searching will yield other partitions of 10 as {5,4,1}, {5,3,2}. No others can exist.

The point is, this operation can easily be defined as a recursive function. With careful coding, one might even use memoization, to avoid recomputing that which we have seen before.

Answered By: user85109

Here is one way to solve this problem:

def sum_to_n(n, size, limit=None):
    """Produce all lists of `size` positive integers in decreasing order
    that add up to `n`."""
    if size == 1:
        yield [n]
        return
    if limit is None:
        limit = n
    start = (n + size - 1) // size
    stop = min(limit, n - size + 1) + 1
    for i in range(start, stop):
        for tail in sum_to_n(n - i, size - 1, i):
            yield [i] + tail

You can use it like this.

for partition in sum_to_n(6, 3):
    print partition

[2, 2, 2]
[3, 2, 1]
[4, 1, 1]
Answered By: Jason Orendorff

Your question can be rephrased like this :

Given a number N, find all sets [S1, S2, S3 …..] where the sum of each set equals N. The size of the sets are given by the number L.


First let’s consider the case of L=2.This means that you can have the following sets

(9,1) , (8,2), (7,3) , (6,4) , (5,5)

I’ll call this the base solution and you’ll soon see why.

Let’s change our L to 3 now and redo our answer :

So let’s consider the number 9. Does there exist such a list L such that sum(L) + 9 = 10 ? The obvious answer is No, but what’s interesting here is not the answer but the question itself. We are basically asking for a set of 2 elements that can be summed up to be the number 1. This is the same problem that was solved by the base solution.

So therefore for each number x in [9,8,7,6,5,4,3,2,1] you try to find a set [a,b]such that x+a+b = 10.

This isn’t a complete answer, but the idea is that you see the pattern here, and use recursion to calculate the base case as done above and then figure out the recursive call that will complete your solution. Good luck!

Answered By: Arnab Datta

Thank you @Jason Orendorff – I further developed your function, because I needed a few more options.

It`s now possible to specify min and max summand count, and to permutate the combinations.

Be careful with max_summand_count: If you set it too high, you end up in recursion hell 🙂

from itertools import permutations

def get_all_possible_summands_to_n(n, min_summand_count=1, max_summand_count=5, min_summand=1, max_summand=None, permutate=False):
    if min_summand_count < 1:
        raise ValueError("min_summand_count may not be below 1")

    if min_summand < 1:
        raise ValueError("min_summand may not be below 1")
    
    if not max_summand_count:
        max_summand_count = n

    if max_summand is None:
            max_summand = n
    
    for size in range(min_summand_count, max_summand_count + 1):
        if size == 1 and n <= max_summand:
            yield [n]
            continue
        
        start = (n + size - 1) // size
        stop = min(max_summand, n - size + 1) + 1

        for i in range(start, stop):
            if (size-1) > 0:                
                for tail in get_all_possible_summands_to_n(n - i, size - 1, size - 1, min_summand, i): 
                    result = list([i] + list(tail))

                    if [c for c in result if c >=min_summand] == result:
                        if permutate:
                            for combination in list(set(permutations(result, len(result)))):
                                yield list(combination)
                        else:                   
                            yield result

Here all all the tests it passed:

def test_get_all_possible_summands_to_n(self):

    # BASICS
    self.assertEqual(
        list(get_all_possible_summands_to_n(3)),
        [[3], [2,1], [1,1,1]]
    )

    # permutation
    self.assertEqual(
        list(get_all_possible_summands_to_n(3, permutate=True)),
        [[3], [1,2], [2,1], [1,1,1]]
    )

    # MIN / MAX SUMMAND
    self.assertEqual(
        list(get_all_possible_summands_to_n(4)),
        [[4], [2, 2], [3, 1], [2, 1, 1], [1, 1, 1, 1]]
    )

    # min summand
    self.assertEqual(
        list(get_all_possible_summands_to_n(4, min_summand=2)),
        [[4], [2, 2]]
    )

    self.assertEqual(
        list(get_all_possible_summands_to_n(4, min_summand=3)),
        [[4]]
    )

    # max summand
    self.assertEqual(
        list(get_all_possible_summands_to_n(4, max_summand=2)),
        [[2, 2], [2, 1, 1], [1, 1, 1, 1]]
    )

    self.assertEqual(
        list(get_all_possible_summands_to_n(4, max_summand=3)),
        [[2, 2], [3, 1], [2, 1, 1], [1, 1, 1, 1]]
    )

    # min / max summand combination
    self.assertEqual(
        list(get_all_possible_summands_to_n(5)),
        [[5], [3, 2], [4, 1], [2, 2, 1], [3, 1, 1], [2, 1, 1, 1], [1, 1, 1, 1, 1]]
    )

    self.assertEqual(
        list(get_all_possible_summands_to_n(5, min_summand=2, max_summand=3)),
        [[3, 2]]
    )

    self.assertEqual(
        list(get_all_possible_summands_to_n(6, min_summand=3, max_summand=3)),
        [[3, 3]]
    )

    # MIN / MAX SUMMAND COUND
    self.assertEqual(
        list(get_all_possible_summands_to_n(6)),
        [
            [6],
            [3, 3],
            [4, 2],
            [5, 1],
            [2, 2, 2],
            [3, 2, 1],
            [4, 1, 1],
            [2, 2, 1, 1],
            [3, 1, 1, 1],
            [2, 1, 1, 1, 1]
        ]
    )

    # min summand count
    self.assertEqual(
        list(get_all_possible_summands_to_n(6, min_summand_count=3)),
        [                
            [2, 2, 2],
            [3, 2, 1],
            [4, 1, 1],
            [2, 2, 1, 1],
            [3, 1, 1, 1],
            [2, 1, 1, 1, 1]
        ]
    )

    # max summand count
    self.assertEqual(
        list(get_all_possible_summands_to_n(6, max_summand_count=3)),
        [
            [6],
            [3, 3],
            [4, 2],
            [5, 1],
            [2, 2, 2],
            [3, 2, 1],
            [4, 1, 1],
        ]
    )

    # min max summand count combination
    self.assertEqual(
        list(get_all_possible_summands_to_n(6, min_summand_count=2, max_summand_count=3)),
        [          
            [3, 3],
            [4, 2],
            [5, 1],      
            [2, 2, 2],
            [3, 2, 1],
            [4, 1, 1],                
        ]
    ) 

    self.assertEqual(
        list(get_all_possible_summands_to_n(6, min_summand_count=3, max_summand_count=3)),
        [                
            [2, 2, 2],
            [3, 2, 1],
            [4, 1, 1],                
        ]
    )              

    # ALL COMBINATIONS
    self.assertEqual(
        list(get_all_possible_summands_to_n(12, min_summand_count=2, max_summand_count=3, min_summand=2, max_summand=3)),
        []
    )

    self.assertEqual(
        list(
            get_all_possible_summands_to_n(
                12, 
                min_summand_count=4, 
                max_summand_count=5, 
                min_summand=2, 
                max_summand=4
            )
        ),
        [
            [3, 3, 3, 3],
            [4, 3, 3, 2],
            [4, 4, 2, 2],
            [3, 3, 2, 2, 2],
            [4, 2, 2, 2, 2]
        ]
    )

Feel free to optimise it`s performance and elegancy 🙂 – I`m sure there is some potential to do so

Answered By: user1383029
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.