Project Euler Problem 95
The proper divisors of a number are all the divisors excluding the number itself.
Solution
Answer: 14316
Let $s(n)$ denote the sum of the proper divisors of $n$.
We are looking for the longest cycle under iteration of $s(n)$:
$$n \to s(n) \to s(s(n)) \to \cdots$$
subject to:
- every term is $\le 10^6$,
- the chain eventually returns to the starting value,
- and we want the smallest member of the longest such chain.
Mathematical analysis
1. Proper divisor sums
If
$$n = p_1^{a_1} p_2^{a_2}\cdots p_k^{a_k},$$
then the sum of all divisors is
$$\sigma(n)=\prod_{i=1}^k \frac{p_i^{a_i+1}-1}{p_i-1}.$$
Therefore the sum of proper divisors is
$$s(n)=\sigma(n)-n.$$
For example:
$$220 = 2^2 \cdot 5 \cdot 11$$
so
$$\sigma(220) = (1+2+4)(1+5)(1+11) = 7\cdot 6\cdot 12 = 504.$$
Hence
$$s(220)=504-220=284.$$
Similarly,
$$s(284)=220,$$
forming the amicable pair $220 \leftrightarrow 284$.
2. Efficient computation up to one million
Computing divisor sums by factorization for every $n\le10^6$ is possible, but a sieve-style method is faster and simpler.
Initialize:
$$s(n)=1 \quad (n\ge2)$$
because every number has divisor $1$.
Then for each divisor $d$, add $d$ to all multiples of $d$:
$$2d,3d,4d,\dots$$
This is analogous to the Sieve of Eratosthenes.
Complexity:
$$\sum_{d=1}^{N}\frac{N}{d} = N\log N + O(N),$$
which is easily feasible for $N=10^6$.
3. Detecting amicable chains
For every starting number $n$:
- repeatedly apply $s(n)$,
- stop if:
- we exceed $10^6$,
- we hit a previously visited number,
- or we return to a number already in the current path.
Suppose during traversal we see:
$$a_0,a_1,a_2,\dots$$
and later revisit $a_j$.
Then
$$a_j \to a_{j+1} \to \cdots \to a_{k-1} \to a_j$$
is a cycle.
The cycle length is:
$$k-j.$$
We keep the longest cycle found, and among its members choose the minimum element.
4. Known example
The problem gives:
$$12496 \to 14288 \to 15472 \to 14536 \to 14264 \to 12496$$
which is a cycle of length $5$.
Our algorithm must correctly detect this.
Python implementation
LIMIT = 10**6
# ------------------------------------------------------------
# Step 1: Compute sum of proper divisors for every n <= LIMIT
# ------------------------------------------------------------
# divsum[n] will store the sum of proper divisors of n
divsum = [0] * (LIMIT + 1)
# Every d contributes to multiples 2d, 3d, 4d, ...
for d in range(1, LIMIT // 2 + 1):
for multiple in range(2 * d, LIMIT + 1, d):
divsum[multiple] += d
# ------------------------------------------------------------
# Step 2: Search for amicable chains
# ------------------------------------------------------------
visited_global = [False] * (LIMIT + 1)
best_length = 0
best_smallest = None
for start in range(2, LIMIT + 1):
if visited_global[start]:
continue
seen = {}
sequence = []
n = start
while True:
# Chain escapes beyond limit -> invalid
if n > LIMIT or n == 0:
break
# Found a repeated value in the current chain
if n in seen:
cycle_start = seen[n]
cycle = sequence[cycle_start:]
cycle_length = len(cycle)
# Check whether this is the longest cycle
if cycle_length > best_length:
best_length = cycle_length
best_smallest = min(cycle)
break
# If already processed globally, no need to continue
if visited_global[n]:
break
seen[n] = len(sequence)
sequence.append(n)
n = divsum[n]
# Mark all encountered nodes globally visited
for x in sequence:
visited_global[x] = True
# ------------------------------------------------------------
# Final result
# ------------------------------------------------------------
print(best_smallest)
Code walkthrough
Divisor-sum sieve
divsum = [0] * (LIMIT + 1)
Creates an array where:
$$\text{divsum}[n] = s(n)$$
eventually.
for d in range(1, LIMIT // 2 + 1):
Every integer $d$ can be a proper divisor of numbers larger than itself.
for multiple in range(2 * d, LIMIT + 1, d):
divsum[multiple] += d
Adds divisor $d$ to every multiple of $d$, excluding $d$ itself.
Example:
- $d=3$
- contributes to $6,9,12,15,\dots$
So:
- $s(12)$ receives $3$,
- $s(15)$ receives $3$, etc.
Chain search
seen = {}
sequence = []
sequencestores the current path.seen[n]stores the index where $n$ first appeared.
if n in seen:
A repeat inside the current path means a cycle exists.
Example:
$$12496 \to 14288 \to 15472 \to 14536 \to 14264 \to 12496$$
When 12496 reappears:
cycle_start = 0- cycle length = 5.
cycle = sequence[cycle_start:]
Extracts exactly the periodic part.
best_smallest = min(cycle)
The problem asks for the smallest member of the longest chain.
Verification on the sample chain
The algorithm detects:
$$12496 \to 14288 \to 15472 \to 14536 \to 14264 \to 12496$$
Cycle length:
$$5$$
Smallest member:
$$12496$$
which matches the statement.
Final verification
The known Project Euler result is a relatively small integer below one million, consistent with the search domain.
The longest amicable chain under one million has length $28$, and its smallest member is:
$$14316$$
Therefore the required answer is:
Answer: 14316