All Euler problems
Project Euler

Amicable Chains

Define s(n) = sigma(n) - n, the sum of proper divisors of n. An amicable chain of length k is a cycle n_1 -> n_2 ->... -> n_k -> n_1 under the map s, where all n_i are distinct. Find the smallest e...

Source sync Apr 19, 2026
Problem #0095
Level Level 03
Solved By 16,658
Languages C++, Python
Answer 14316
Length 462 words
sequencelinear_algebranumber_theory

Problem Statement

This archive keeps the full statement, math, and original media on the page.

The proper divisors of a number are all the divisors excluding the number itself. For example, the proper divisors of $28$ are $1$, $2$, $4$, $7$, and $14$. As the sum of these divisors is equal to $28$, we call it a perfect number

Interestingly the sum of the proper divisors of $220$ is $284$ and the sum of the proper divisors of $284$ is $220$, forming a chain of two numbers. For this reason, $220$ and $284$ are called an amicable pair.

Perhaps less well known are longer chains. For example, starting with $12496$, we form a chain of five numbers: $$12496 \to 14288 \to 15472 \to 14536 \to 14264 (\to 12496 \to \cdots)$$ Since this chain returns to its starting point, it is called an amicable chain.

Find the smallest member of the longest amicable chain with no element exceeding one million.

Problem 95: Amicable Chains

Mathematical Foundation

Definition 1 (Sum of proper divisors). For n1n \ge 1, define

s(n)=σ(n)n=dn1d<nd.s(n) = \sigma(n) - n = \sum_{\substack{d \mid n \\ 1 \le d < n}} d.

Note s(1)=0s(1) = 0.

Definition 2 (Amicable chain). An amicable chain of length k1k \ge 1 is a sequence of kk distinct positive integers (n1,n2,,nk)(n_1, n_2, \ldots, n_k) satisfying s(ni)=ni+1s(n_i) = n_{i+1} for 1i<k1 \le i < k and s(nk)=n1s(n_k) = n_1. The cases k=1k = 1 (perfect numbers) and k=2k = 2 (amicable pairs) are classical.

Theorem 1 (Divisor sum sieve). The function s(n)s(n) for all nNn \le N can be computed in O(NlogN)O(N \log N) time and O(N)O(N) space.

Proof. Initialize an array s[1..N]=0s[1..N] = 0. For each d=1,2,,Nd = 1, 2, \ldots, N, add dd to s[kd]s[kd] for k=2,3,,N/dk = 2, 3, \ldots, \lfloor N/d \rfloor. After this process, s[n]=dn,d<nds[n] = \sum_{\substack{d \mid n, d < n}} d because every proper divisor d<nd < n of nn is enumerated exactly once when we process divisor dd and add it to s[d(n/d)]=s[n]s[d \cdot (n/d)] = s[n].

The total number of additions is d=1N(N/d1)d=1NN/d=NHN=O(NlogN)\sum_{d=1}^{N}(\lfloor N/d \rfloor - 1) \le \sum_{d=1}^{N} N/d = N H_N = O(N \log N), where HN=d=1N1/dH_N = \sum_{d=1}^{N} 1/d is the NN-th harmonic number. \square

Lemma 1 (Functional iteration and cycle detection). Let f:SSf: S \to S be a function on a finite set SS. For any x0Sx_0 \in S, the sequence x0,f(x0),f2(x0),x_0, f(x_0), f^2(x_0), \ldots eventually enters a cycle. If we track visited elements and the sequence returns to x0x_0, the elements visited form an amicable chain containing x0x_0.

Proof. Since SS is finite, the sequence must revisit some element. If the first revisited element is x0x_0 itself, the trajectory from x0x_0 back to x0x_0 forms a cycle, which is an amicable chain by definition. If the first revisited element is some xjx0x_j \ne x_0, then x0x_0 is on a “tail” leading into the cycle at xjx_j and is not part of any cycle. \square

Theorem 2 (Amortized complexity of chain detection). Using a visited-flag array, the total work for chain detection across all starting points n=2,,Nn = 2, \ldots, N is O(N)O(N).

Proof. Once a number is marked visited, it is never the starting point of a new exploration and causes any exploration encountering it to terminate immediately. Each number is visited at most once as a “fresh” element during some exploration and at most once as a “termination trigger” for a different exploration. Hence the total number of steps across all explorations is at most 2N2N. \square

Editorial

The map ns(n)n \mapsto s(n) turns the problem into a functional graph on the integers up to 10610^6. The first phase builds that graph efficiently with a divisor-sum sieve, so after that every node has exactly one outgoing edge.

The second phase is then standard cycle hunting in a functional graph. Starting from each unseen value, we follow successors until one of three things happens: the chain leaves the allowed range, it reaches a node already settled by an earlier exploration, or it revisits a node from the current walk. Only the third case creates a new amicable chain. The local position map identifies the cycle portion exactly, and the global visited array prevents the same tail from being explored again.

Pseudocode

Compute the sum of proper divisors $s(n)$ for every $n \le N$ with a sieve.

Create a global visited array.
Set the best chain length and best minimum element to 0.

For each starting value from 2 to $N$:
    If it is already globally visited, skip it

    Follow the map $n \mapsto s(n)$, recording:
        the current chain in order
        the position where each value first appears

    Stop when the chain leaves the range, reaches a globally visited value, or repeats a value from the current walk

    If a repeat occurred inside the current walk:
        extract the cycle part
        update the best answer if this cycle is longer

    Mark every value from the explored walk as globally visited

Return the smallest element from the longest cycle found.

Complexity Analysis

Time: O(NlogN)O(N \log N) for the sieve (Theorem 1) plus O(N)O(N) for cycle detection (Theorem 2). Total: O(NlogN)O(N \log N).

Space: O(N)O(N) for the arrays s[]s[\cdot] and visited[][\cdot].

Answer

14316\boxed{14316}

Code

Each problem page includes the exact C++ and Python source files from the local archive.

C++ project_euler/problem_095/solution.cpp
#include <bits/stdc++.h>
using namespace std;

int main() {
    const int LIMIT = 1000000;

    // Phase 1: Sieve for sum of proper divisors
    vector<int> s(LIMIT + 1, 0);
    for (int i = 1; i <= LIMIT; i++)
        for (int j = 2 * i; j <= LIMIT; j += i)
            s[j] += i;

    // Phase 2: Detect longest amicable chain
    vector<bool> visited(LIMIT + 1, false);
    int bestLen = 0, bestMin = 0;

    for (int start = 2; start <= LIMIT; start++) {
        if (visited[start]) continue;

        vector<int> chain;
        unordered_map<int, int> positions;
        int n = start;

        while (n >= 1 && n <= LIMIT && !visited[n] && !positions.count(n)) {
            positions[n] = (int)chain.size();
            chain.push_back(n);
            n = s[n];
        }

        // Check for cycle
        if (n >= 1 && n <= LIMIT && positions.count(n)) {
            int cycleStart = positions[n];
            int cycleLen = (int)chain.size() - cycleStart;
            int minElem = *min_element(chain.begin() + cycleStart, chain.end());
            if (cycleLen > bestLen) {
                bestLen = cycleLen;
                bestMin = minElem;
            }
        }

        for (int x : chain) visited[x] = true;
    }

    cout << bestMin << endl;
    return 0;
}