Project Euler 23 - No matter what I do, my answer is far too big
<p>Problem 23 asks for the sum of all numbers "not the sum of two abundant numers". We need only check numbers smaller than 28123. An integer n is abundant if its proper divisors sum to an integer greater than n - 12 is the first abundant number since 1+2+3+4+6 = 16 > 12. This makes 24 the smallest number which is a sum of 2 abundant numbers, so we want to exclude it from the sum.</p>

<p>Here's my python code, which I've tried many variations of, but using the same idea:</p>

<pre><code>import math

def divisors_of(n):
    divs=[1]
    for i in range(2,int(math.sqrt(n))+1):
        if i**2 == n:
            divs.append(i)
        elif n%i == 0:
            divs.append(i)
            divs.append(n//i)
    return divs

def is_abun(n):
    return sum(divisors_of(n))&gt;n

numbers=[i for i in range(1,28123)]

for i in numbers:
    for j in range(1,i):
        if is_abun(j) and is_abun(i-j):
         numbers.remove(i)
         break

print(sum(numbers))
</code></pre>

<p>The idea is simply that if j and i-j are abundant numbers, then of course j + (i-j) = i is a sum of abundant numbers, so I remove it from my list of numbers. Then I sum over all remaining numbers. I don't see how this could possibly fail. However, the result should be (gotten from another source) 4179871, while I always get something in the ballpark of 197711987, which is over 47 times too large. </p>

<p>I really cannot understand how this fails to generate the correct result. There must be some line which isn't doing what I think it is, but everything here is just <em>too simple</em> for me to be suspicious of them. I thought the most likely error would be in calculating the abundant numbers, but I am almost completely sure that part's correct (if I get the code as is to generate a list of abundant numbers less than 28123, I get a list of length 6965, which is how many abundant numbers less than 28123 WolframAlpha claims).</p>

<p>The problem is that you are modifying the list <code>numbers</code> while you are iterating over it. This results in you removing less numbers than what you expect.</p>

<p>You should iterate over a copy of it:</p>

<pre><code>for i in list(numbers):
    for j in range(1,i):
        if is_abun(j) and is_abun(i-j):
         numbers.remove(i)
         break
</code></pre>

<p>To understand what's happening consider this:</p>

<pre><code>&gt;&gt;&gt; numbers = list(range(10))
&gt;&gt;&gt; for i in numbers:
...     if i%2 == 0:
...         numbers.remove(i)
...     print(i)
... 
0
2
4
6
8
</code></pre>

<p>As you can see the <code>for</code> loop only iterates over even numbers. That's because the <code>for</code> loop above is <em>roughly</em> equivalent to the following:</p>

<pre><code>i = 0
while i &lt; len(numbers):
    number = numbers[i]
    if number % 2 == 0:
        numbers.remove(number)
    print(number)
    i += 1
</code></pre>

<p>So the first time you have <code>i = 0</code> and thus <code>number = 0</code>, you remove the <code>0</code> from <code>numbers</code> so now you have <code>numbers = [1,2,3,4,..., 9]</code>. Note how the value <code>1</code> ended up in position <code>0</code>, so the next iteration we have <code>i = 1</code> and <code>numbers[1] == [1,2,3,4,...,9][1] == 2</code> so we have skipped the number <code>1</code>!</p>

<p>Everytime you remove a number the list is modified and if this modification leads to the items being shifted to the left you are effectively skipping them. That's why you want to iterate over a <em>copy</em> of the list: in this way the modifications to the original list do not affect the numbers "yielded" during iteration..</p>

python