Project Euler Problem 21 Statement
Let d(n) be defined as the sum of proper divisors of n (numbers less than n which divide evenly into n).
If d(a) = b and d(b) = a, where a ≠ b, then a and b are an amicable pair and each of a and b are called amicable numbers.
For example, the proper divisors of 220 are 1, 2, 4, 5, 10, 11, 20, 22, 44, 55 and 110; therefore d(220) = 284. The proper divisors of 284 are 1, 2, 4, 71 and 142; so d(284) = 220.
Evaluate the sum of all the amicable numbers under 10000.
This problem’s description can lead to some confusion because we want to add amicable numbers – not amicable pairs – below some limit. For a limit of 10000 it’s no problem and most solutions add the pairs found and are quickly rewarded with an accepted answer. But there is something nefariously hidden in this over simplified exercise. Let me first show you a list of amicable numbers below 11000 from OEIS A259180:
220, 284, 1184, 1210, 2620, 2924, 5020, 5564, 6232, 6368, 10744, 10856
Well, for 10000 we can easily calculate pairs because both pairs of an amicable set are well below 10000. But what if we want amicable numbers below 5200? Most programs listed in the Project Euler forum would fail to give a correct answer because 5020 can only be found if its complement 5564 is also found and a search limit of 5200 isn’t deep enough to find both.
A better method
To prevent this potential problem, we calculate sums of proper divisors for integers beyond the intended search limit to make sure we collect all the amicable numbers for a correct answer. An amicable pair that exceed our search limit is considered suitable.
By finding a fast way to sieve the sum of proper divisors we both solve this problem and the somewhat more challenging HackerRank version.
We begin by initializing a list,
ds, to all ones,
L elements long and fill it by sieving the sum of proper divisors. The primary
i loop only needs to reach the square root of our limit,
L (11000 in this case – enough to find the next pair outside our limit of 10000).
from math import sqrt L = 11000 ds = *L for i in range(2, int(sqrt(L))): for j in range(i+1, L//i): ds[i*j]+= i+j
Then, filter from the list,
ds, amicable pairs by appending
i to the list
an. Remember, for numbers to qualify as amicable pairs they must meet the criteria d(d(a)) = a, where d(a) < a.
Note: Adding a list (
[ds[i], i]) to another list (
an) is the same as appending.
an =  for i in range(2, L): if ds[i] < i and ds[ds[i]] == i: an+= [ds[i], i]
Finally, sum amicable numbers from the list
an that are less than
N. Keep mindful of a reasonable search limit for the depth of sums of proper divisors long since rehearsed.
N = int(input("Limit? ")) print "Amicable sum <",N,"=", sum(a for a in an if a<N)
HackerRank extends the limit from N<10,000 to 1≤N≤100,000 and runs up to a thousand test cases in less than a second. This requires us to pre-calculate the sum of divisors to a limit where both numbers of an amicable pair exceed 100,000.
Python Source Code
Some other results
|2 x 108||32.2||32292177807|