Tim Varley Logo
Tim Varley Principal AI Engineer and Tech Leader
Problem #95 medium

Amicable chains

The proper divisors of a number are all the divisors excluding the number itself. For example, the proper divisors of 2828 are 11, 22, 44, 77, and 1414. As the sum of these divisors is equal to 2828, we call it a perfect number.

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

Perhaps less well known are longer chains. For example, starting with 1249612496, we form a chain of five numbers:

1249614288154721453614264(12496)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.

View on Project Euler

Implementations

cpp
#include <iostream>
#include <vector>
#include <algorithm>
#include <set>
const int MAX = 1000000;
std::vector<int> sum_div(MAX + 1, 0);
void compute_sum_div() {
for (int i = 1; i <= MAX; ++i) {
for (int j = i * 2; j <= MAX; j += i) {
sum_div[j] += i;
}
}
}
long long amicable_chains() {
compute_sum_div();
int max_length = 0;
int min_member = MAX;
std::vector<bool> visited(MAX + 1, false);
for (int i = 2; i <= MAX; ++i) {
if (visited[i] || sum_div[i] > MAX) continue;
std::vector<int> chain;
std::set<int> chain_set;
int current = i;
while (current <= MAX && sum_div[current] != current && chain_set.find(current) == chain_set.end()) {
chain.push_back(current);
chain_set.insert(current);
current = sum_div[current];
}
// Check if we found a cycle (current is already in chain)
if (chain_set.find(current) != chain_set.end() && chain.size() > 1) {
// Find where the cycle starts
int cycle_start = -1;
for (int j = 0; j < (int)chain.size(); ++j) {
if (chain[j] == current) {
cycle_start = j;
break;
}
}
int cycle_length = chain.size() - cycle_start;
if (cycle_length > max_length) {
max_length = cycle_length;
// Find smallest member in the cycle
int smallest = MAX;
for (size_t j = cycle_start; j < chain.size(); ++j) {
if (chain[j] < smallest) smallest = chain[j];
visited[chain[j]] = true; // Mark cycle numbers as visited
}
min_member = smallest;
}
}
}
return min_member;
}

Solution Notes

Mathematical Background

Amicable chains are sequences where each number is the sum of proper divisors of the previous. A chain forms a cycle when it returns to a starting point. The problem seeks the longest such chain below 1,000,000 and its smallest member.

Algorithm Analysis

Precompute sum of proper divisors for all numbers up to limit. Traverse chains from each unvisited number, using visited array to avoid redundancy and detecting cycles when encountering previously seen numbers in current chain.

Performance

Varies by language: ~500ms in C++/Go/Java/JavaScript, ~2s in Python, ~5s in Rust due to different optimization levels.

Key Insights

  • Efficient divisor sum precomputation using sieve-like method
  • Cycle detection prevents infinite loops and identifies valid chains
  • Visited array ensures each number processed only once

Educational Value

Demonstrates graph traversal in number theory, cycle detection algorithms, and optimization of computational number theory problems across multiple programming paradigms.

Problem #95 is taken from Project Euler and licensed under CC BY-NC-SA 4.0.