Problem 802: Iterated Composition
View on Project EulerProject Euler Problem 802 Solution
EulerSolve provides an optimized solution for Project Euler Problem 802, Iterated Composition, with C++, Python, Java, and a step-by-step mathematical explanation.
Problem Summary For Problem 802, the C++, Python, and Java implementations evaluate the arithmetic quantity $S(N)=\sum_{k=1}^{N}\mu(k)\,2^{\left\lfloor N/k\right\rfloor}\pmod{M},\qquad M=1020340567,$ with the final input \(N=10^7\). The difficulty is not the modular arithmetic itself, but organizing the computation so that Möbius values, prefix sums, and repeated floor quotients are reused instead of recomputed. Mathematical Approach The implementations treat the problem as a Möbius-weighted transform of the basic sequence \(A(t)=2^t\). Two ideas drive the solution: compute \(\mu(k)\) efficiently for every \(k\le N\), and exploit the fact that \(\left\lfloor N/k\right\rfloor\) stays constant on long intervals. Step 1: Isolate the arithmetic core The exact quantity being accumulated is $S(N)=\sum_{k=1}^{N}\mu(k)A\!\left(\left\lfloor\frac{N}{k}\right\rfloor\right),\qquad A(t)=2^t.$ Each index \(k\) contributes two pieces: a Möbius weight \(\mu(k)\in\{-1,0,1\}\) and a power of two determined only by the quotient \(\left\lfloor N/k\right\rfloor\). This already suggests separating number-theoretic preprocessing from the final accumulation stage....
Detailed mathematical approach
Problem Summary
For Problem 802, the C++, Python, and Java implementations evaluate the arithmetic quantity
$S(N)=\sum_{k=1}^{N}\mu(k)\,2^{\left\lfloor N/k\right\rfloor}\pmod{M},\qquad M=1020340567,$
with the final input \(N=10^7\). The difficulty is not the modular arithmetic itself, but organizing the computation so that Möbius values, prefix sums, and repeated floor quotients are reused instead of recomputed.
Mathematical Approach
The implementations treat the problem as a Möbius-weighted transform of the basic sequence \(A(t)=2^t\). Two ideas drive the solution: compute \(\mu(k)\) efficiently for every \(k\le N\), and exploit the fact that \(\left\lfloor N/k\right\rfloor\) stays constant on long intervals.
Step 1: Isolate the arithmetic core
The exact quantity being accumulated is
$S(N)=\sum_{k=1}^{N}\mu(k)A\!\left(\left\lfloor\frac{N}{k}\right\rfloor\right),\qquad A(t)=2^t.$
Each index \(k\) contributes two pieces: a Möbius weight \(\mu(k)\in\{-1,0,1\}\) and a power of two determined only by the quotient \(\left\lfloor N/k\right\rfloor\). This already suggests separating number-theoretic preprocessing from the final accumulation stage.
Step 2: Recall what the Möbius function contributes
The Möbius function is defined by
$\mu(1)=1,$
$\mu(n)=0 \text{ if } p^2\mid n \text{ for some prime } p,$
$\mu(n)=(-1)^r \text{ if } n \text{ is a product of } r \text{ distinct primes}.$
So integers containing a squared prime factor disappear entirely, while square-free integers contribute with alternating sign. The sum is therefore an inclusion-exclusion style filter applied to the sequence \(2^{\left\lfloor N/k\right\rfloor}\).
Step 3: Replace raw Möbius values by prefix sums
Define the Mertens prefix function
$\mathcal{M}(x)=\sum_{k\le x}\mu(k).$
Then any interval sum can be written as
$\sum_{k=a}^{b}\mu(k)=\mathcal{M}(b)-\mathcal{M}(a-1).$
This converts a sum of many Möbius values into a constant-time difference. That becomes useful as soon as many adjacent indices share the same quotient \(\left\lfloor N/k\right\rfloor\).
Step 4: Group indices by equal floor quotients
Fix a starting index \(i\) and set
$q=\left\lfloor\frac{N}{i}\right\rfloor.$
All indices with this same quotient form the interval
$i\le k\le j,\qquad j=\left\lfloor\frac{N}{q}\right\rfloor.$
For every \(k\in[i,j]\), the power of two is identical:
$2^{\left\lfloor N/k\right\rfloor}=2^q.$
Hence the entire block contributes
$\sum_{k=i}^{j}\mu(k)\,2^{\left\lfloor N/k\right\rfloor}=\left(\mathcal{M}(j)-\mathcal{M}(i-1)\right)2^q.$
After one block is processed, the next unexplored index is \(j+1\). The number of distinct quotients is only about \(2\sqrt N\), so the accumulation stage becomes much shorter than a literal term-by-term pass.
Step 5: Precompute the powers only once
The same exponents appear repeatedly, so the implementations build the table
$P(t)=2^t\bmod M,\qquad 0\le t\le N.$
Every quotient block then needs only one lookup and one modular multiplication. No repeated exponentiation is performed inside the main block sweep.
Worked Example: \(N=10\)
For the first ten Möbius values we have
$\mu(1),\dots,\mu(10)=1,-1,-1,0,-1,1,-1,0,0,1.$
The quotient blocks are
$\begin{aligned} k=1 &: \left\lfloor 10/k\right\rfloor=10,\quad \mathcal{M}(1)-\mathcal{M}(0)=1,\quad &&1\cdot 2^{10}=1024,\\ k=2 &: \left\lfloor 10/k\right\rfloor=5,\quad \mathcal{M}(2)-\mathcal{M}(1)=-1,\quad &&-1\cdot 2^5=-32,\\ k=3 &: \left\lfloor 10/k\right\rfloor=3,\quad \mathcal{M}(3)-\mathcal{M}(2)=-1,\quad &&-1\cdot 2^3=-8,\\ 4\le k\le 5 &: \left\lfloor 10/k\right\rfloor=2,\quad \mathcal{M}(5)-\mathcal{M}(3)=-1,\quad &&-1\cdot 2^2=-4,\\ 6\le k\le 10 &: \left\lfloor 10/k\right\rfloor=1,\quad \mathcal{M}(10)-\mathcal{M}(5)=1,\quad &&1\cdot 2^1=2. \end{aligned}$
Adding the block contributions gives
$1024-32-8-4+2=982.$
This example makes the structure clear: five blocks replace ten individual terms, and the savings become much larger when \(N\) is large.
How the Code Works
The C++, Python, and Java implementations follow the same pipeline. First they build Möbius values up to \(N\) with a linear sieve that also records the smallest prime dividing each integer. This produces the correct sign for square-free numbers and automatically assigns zero to integers divisible by a squared prime.
Next the implementation forms a cumulative Mertens array and, independently, a table of powers \(2^t\bmod M\) for every \(0\le t\le N\). With those two tables ready, the final loop no longer evaluates the sum term by term. Instead it jumps from one maximal quotient block to the next, computes the total Möbius weight of that block by a prefix-sum difference, multiplies by the precomputed power of two for the shared quotient, and accumulates the result modulo \(M\).
Because some block sums are negative, the modular arithmetic is normalized before or after multiplication, depending on the language. That keeps intermediate values safe while preserving the exact arithmetic meaning of the formula.
Complexity Analysis
The linear sieve runs in \(O(N)\) time and stores \(O(N)\) data. Building the Mertens prefix sums and the power table also costs \(O(N)\) time and \(O(N)\) memory. The final quotient-block sweep touches only the distinct values of \(\left\lfloor N/k\right\rfloor\), so that stage is \(O(\sqrt N)\). Overall the method is \(O(N)\) time and \(O(N)\) memory, with preprocessing dominating for \(N=10^7\).
Footnotes and References
- Problem page: https://projecteuler.net/problem=802
- Möbius function: Wikipedia — Möbius function
- Mertens function: Wikipedia — Mertens function
- Linear sieve for multiplicative functions: cp-algorithms — Linear Sieve
- Floor-division grouping in divisor sums: Wikipedia — Divisor summatory function