Totient Chains
Let phi denote Euler's totient function. The totient chain of n is n -> phi(n) -> phi(phi(n)) ->... -> 1. The length of the chain is the number of elements, including both n and 1. For example, the...
Problem Statement
This archive keeps the full statement, math, and original media on the page.
Let \(\phi \) be Euler’s totient function, i.e. for a natural number \(n\), \(\phi (n)\) is the number of \(k\), \(1 \le k \le n\), for which \(\gcd (k, n) = 1\).
By iterating \(\phi \), each positive integer generates a decreasing chain of numbers ending in \(1\).
E.g. if we start with \(5\) the sequence \(5,4,2,1\) is generated.
Here is a listing of all chains with length \(4\): \begin {align*} 5,4,2,1&\\ 7,6,2,1&\\ 8,4,2,1&\\ 9,6,2,1&\\ 10,4,2,1&\\ 12,4,2,1&\\ 14,6,2,1&\\ 18,6,2,1 \end {align*}
Only two of these chains start with a prime, their sum is \(12\).
What is the sum of all primes less than \(40000000\) which generate a chain of length \(25\)?
Problem 214: Totient Chains
Mathematical Development
Theorem 1 (Euler’s product formula). For with distinct primes ,
Proof. By inclusion-exclusion on , the count of integers coprime to is
Lemma 1 (Strict decrease). For all , .
Proof. If , then has at least one prime factor , so
Theorem 2 (Chain recurrence). If denotes the totient-chain length, then and
for all .
Proof. The chain of is followed by the chain of . Since by Lemma 1, repeated iteration must eventually reach 1.
Lemma 2 (Totient sieve correctness). Initialize for all . For each prime , updating every multiple by
produces the correct totient values for all .
Proof. Each prime divisor of contributes exactly one factor in Euler’s product formula, and the sieve applies those factors once per prime divisor.
Lemma 3 (Prime detection). For , we have if and only if is prime.
Proof. If is prime, then every positive integer below it is coprime to , so . Conversely, a composite has a nontrivial prime factor , so at least the multiples of below are excluded, forcing .
Editorial
The recursion for the chain length is the important structural fact: once is known, the chain length of is just one more than the chain length of a smaller number. That means a sieve order is perfect for this problem, because when we reach , both and are already available.
So we combine three tasks in one pass. First compute the totient values with the standard sieve. Then, in increasing order, use
to fill the chain-length array. Whenever is prime, identified by , check whether the chain length is 25 and add it if it is.
Pseudocode
Set LIMIT = 40,000,000.
Initialize phi[n] = n for 0 <= n < LIMIT.
Initialize chain[1] = 1 and chain[n] = 0 otherwise.
answer = 0
For n from 2 to LIMIT - 1:
If phi[n] = n:
For each multiple m of n:
phi[m] -= phi[m] / n
chain[n] = chain[phi[n]] + 1
If phi[n] = n - 1 and chain[n] = 25:
answer += n
Return answer
Complexity Analysis
- Time: for the totient sieve, plus for the chain updates and prime filtering.
- Space: for the totient table and the chain-length table.
Answer
Code
Each problem page includes the exact C++ and Python source files from the local archive.
#include <bits/stdc++.h>
using namespace std;
int main() {
const int N = 40000000;
// Sieve for Euler's totient
vector<int> phi(N);
iota(phi.begin(), phi.end(), 0); // phi[i] = i
for (int i = 2; i < N; i++) {
if (phi[i] == i) { // i is prime
for (int j = i; j < N; j += i) {
phi[j] = phi[j] / i * (i - 1);
}
}
}
// Compute chain lengths
vector<int> chain(N, 0);
chain[1] = 1;
for (int i = 2; i < N; i++) {
chain[i] = 1 + chain[phi[i]];
}
// Sum primes with chain length 25
long long answer = 0;
for (int i = 2; i < N; i++) {
if (phi[i] == i - 1 && chain[i] == 25) { // i is prime and chain length is 25
answer += i;
}
}
cout << answer << endl;
return 0;
}
from array import array
def solve():
limit = 40_000_000
phi = array("I", range(limit))
chain = bytearray(limit)
chain[1] = 1
answer = 0
for n in range(2, limit):
if phi[n] == n:
for multiple in range(n, limit, n):
phi[multiple] -= phi[multiple] // n
chain[n] = chain[phi[n]] + 1
if phi[n] == n - 1 and chain[n] == 25:
answer += n
print(answer)
if __name__ == "__main__":
solve()