2

I need every combination of three positive integers with the sum of 1000.

This was my attempt but I'm unsure if this is correct since I have no way to validate it.

def getSum():
    l = []
    for x in range(1, 999):
        total = 1000-x
        for y in range(1, 999):
            total = total-y
            if total>0:
                l.append([x, y, total])
    return l

print len(getSum())

I get 28776 different combinations. Is that correct?

hungnguyen
  • 44
  • 1
  • 9
Michi Gruber
  • 254
  • 1
  • 18

4 Answers4

6

Since 1+998+1 and 1+1+998 are not the same thing, there are some incredible amount of combinations:

This line can generate them all:

[(i, 1000-i-k, k) for i in range(1,999) for k in range(1,1000-i)]

Results:

[...
(1, 4, 995),
(1, 3, 996),
(1, 2, 997),
(1, 1, 998),
(2, 997, 1),
(2, 996, 2),
...]

The length of this list is:

498501
Rocky Li
  • 4,615
  • 1
  • 10
  • 24
  • This works flawlessly. Unfortunately, I've never seen this syntax. Could you might explain this to me or give me a hint for what I should search for? Thanks for your answer! – Michi Gruber Feb 14 '19 at 14:54
  • This is list comprehension: https://www.pythonforbeginners.com/basics/list-comprehensions-in-python. In this specific case, there are a double loop within this line: https://stackoverflow.com/questions/1198777/double-iteration-in-list-comprehension – Rocky Li Feb 14 '19 at 14:58
  • 2
    Alternatively, using itertools: `[(a, b-a, 1000-b) for a, b in itertools.combinations(range(1000), 2)]`. That generalises more easily if you want partitions with more elements. – rici Feb 14 '19 at 15:05
  • Makes more than sense and is incredible fast. Thanks a lot! – Michi Gruber Feb 14 '19 at 15:14
  • @Dave range(1, 999) goes up to 998. – Rocky Li Feb 14 '19 at 15:34
  • @rici: That approach should also be faster, since it doesn't need to generate 998 custom `range`s, and has all sorts of optimizations built-in to make it hard to beat with any Python level code (especially since you unpack the results; one of the optimizations is to reuse the same `tuple` each time if no references to the `tuple` remain when the next value is requested). – ShadowRanger Feb 14 '19 at 15:42
  • @RockyLi Thanks! I don't use Python much. This answer is right. The length should be choose(999,2) == 498501 == all ways to put 2 'barriers' in the 1-1000 range excluding endpoints. – Dave Feb 14 '19 at 16:40
2

No, that number is not correct. The problem with your code is this line:

        total = total-y

Here, you decrease total further and further with each value of y that you try, never resetting it to the value after just subtracting x. To fix it, create a new variable, e.g. total2, and use that in the inner loop.

        total2 = total-y

This way, you get 498501 combinations. Also, you can break from the inner loop as soon as total2 < 0.


If you need just the number of combinations: Note that there are N-1 combinations to sum two numbers to N, e.g. for N==4: 1+3, 2+2, 3+1 (assuming you consider 1+3 and 3+1 different). You can extend this to the case of three numbers as partitioning the number in two parts two times. This way, you only need a single loop. And this can be simplified further to an O(1) formula.

Example, with naive approach using product as reference:

>>> N = 100  # to make reference faster
>>> sum(1 for t in product(range(1, N+1), repeat=3) if sum(t)==N)
4851
>>> sum(N-1-i for i in range(1, N-1))
4851
>>> ((N-2)*(N-1))//2
4851

Of course, also works for N = 1000 (or much, much larger):

>>> N = 1000
>>> sum(N-1-i for i in range(1, N-1))
498501
>>> ((N-2)*(N-1))//2
498501
tobias_k
  • 74,298
  • 11
  • 102
  • 155
0

If you treated [1,1,998] and [1,998,1] the same (no unique integers):

def getSum():
    l = []
    for x in range(1, 999):
        total = 1000-x
        for y in range(1, 999):
            total = total-y
            if total>0:
                z = [x, y, total]
                z.sort()
                if z not in l:
                    l.append(z)
return l

a = getSum()
print(len(a))

If you want 3 unique integers:

def getSum():
    l = []
    for x in range(1, 999):
        total = 1000-x
        for y in range(1, 999):
            total = total-y
            if total>0:
                z = [x, y, total]
                z.sort()
                if (z not in l) and (not((len(set(z)) < len(z)))):
                    l.append(z)
    return l

a = getSum()
print(len(a))

Otherwise your code is (in my sense) ok. I haven't check your answer yet...

EDIT : I have checked it using brutal force. The correct answer is actually 498501 if you treated (1,1,998) and (998,1,1) differently. Currently I don't know why...

hungnguyen
  • 44
  • 1
  • 9
  • 1
    Look very carefully at the line `total = total - y`. What does that do the second time through the inner loop? – rici Feb 14 '19 at 15:02
  • 1
    @rici: Mind you, it's a problem with the OP's code as well. – ShadowRanger Feb 14 '19 at 15:45
  • @shadow: yes, it's not a criticism. Rather, a suggestion for the last sentence of this answer. – rici Feb 14 '19 at 16:17
  • @rici oh gosh thank you! I'm not very familiar with catching bugs, especially when I'm lazy and not trying to check the answer. I'm also unfamiliar to reading other people code, especially functions =( Thanks again ... **P.S : What is OP? I'm not English-speaker...** – hungnguyen Feb 15 '19 at 16:08
  • 1
    @hungnguyen "Original Post" (or Original Poster) – rici Feb 15 '19 at 21:25
0

Try this:

def getSum():
    l = []
    for x in range(1, 6):
        for y in range(1, 6):
            total = 6-(y+x)
            if total>0:
                s = set([x, y, total])
                if s not in l:
                    l.append(s)
                    print(x, y, total)
    return l

print (len(getSum()))

This is my algorithm, Although there is better ways. In this case I wrote code for number 6 and printed all combinations to show how it works. You can set 1000 or any number instead of 6 in this code(in 3 position) and ignore print() line.

Heyran.rs
  • 413
  • 1
  • 5
  • 18
  • You could avoid the `if total > 0:` test completely (and save a lot of looping) by just adjusting the end point of your inner loop such that the condition is never violated. – ShadowRanger Feb 14 '19 at 15:44