Problem 97: Large Non-Mersenne Prime
View on Project EulerProject Euler Problem 97 Solution
EulerSolve provides an optimized solution for Project Euler Problem 97, Large Non-Mersenne Prime, with C++, Python, Java, and a step-by-step mathematical explanation.
Problem Summary The number in the problem is $N=28433\cdot 2^{7830457}+1.$ Writing out \(N\) in full would be pointless, because the question asks only for the last ten digits. That means we do not need \(N\) itself; we need only its residue modulo $m=10^{10}.$ So the whole task is to evaluate \(N \bmod m\). Once that modular computation is done, the final ten-digit residue is \(8739992577\). Mathematical Approach The C++, Python, and Java implementations all use the same mathematical reduction: keep every intermediate value modulo \(10^{10}\), compute the huge power with binary exponentiation, and then apply the outer multiplication by \(28433\) and the final \(+1\). Reduce the problem to one modular identity Because congruences respect multiplication and addition, we may reduce the power first: $N \equiv 28433\cdot \left(2^{7830457}\bmod m\right)+1 \pmod m.$ If we set $P\equiv 2^{7830457}\pmod m,$ then the answer is simply $\boxed{(28433\cdot P+1)\bmod 10^{10}.}$ So the real work is concentrated in one object: the residue of \(2^{7830457}\) modulo \(10^{10}\). Binary exponentiation gives the right recurrence The exponent \(7830457\) is large, but it has only 23 binary digits. Binary exponentiation processes that exponent by repeated halving....
Detailed mathematical approach
Problem Summary
The number in the problem is
$N=28433\cdot 2^{7830457}+1.$
Writing out \(N\) in full would be pointless, because the question asks only for the last ten digits. That means we do not need \(N\) itself; we need only its residue modulo
$m=10^{10}.$
So the whole task is to evaluate \(N \bmod m\). Once that modular computation is done, the final ten-digit residue is \(8739992577\).
Mathematical Approach
The C++, Python, and Java implementations all use the same mathematical reduction: keep every intermediate value modulo \(10^{10}\), compute the huge power with binary exponentiation, and then apply the outer multiplication by \(28433\) and the final \(+1\).
Reduce the problem to one modular identity
Because congruences respect multiplication and addition, we may reduce the power first:
$N \equiv 28433\cdot \left(2^{7830457}\bmod m\right)+1 \pmod m.$
If we set
$P\equiv 2^{7830457}\pmod m,$
then the answer is simply
$\boxed{(28433\cdot P+1)\bmod 10^{10}.}$
So the real work is concentrated in one object: the residue of \(2^{7830457}\) modulo \(10^{10}\).
Binary exponentiation gives the right recurrence
The exponent \(7830457\) is large, but it has only 23 binary digits. Binary exponentiation processes that exponent by repeated halving. Start with
$r_0=1,\qquad b_0=2\bmod m,\qquad e_0=7830457.$
At each step, if the current exponent is odd, multiply the accumulated result by the current base; then square the base and halve the exponent:
$ \text{if } e_t \text{ is odd, then } r_{t+1}\equiv r_t b_t \pmod m,\quad \text{otherwise } r_{t+1}=r_t, $
$ b_{t+1}\equiv b_t^2\pmod m,\qquad e_{t+1}=\left\lfloor \frac{e_t}{2}\right\rfloor. $
After about \(\lfloor \log_2 7830457 \rfloor+1=23\) iterations, the exponent becomes zero, so the algorithm finishes very quickly.
The loop invariant that proves correctness
The key invariant is
$r_t\cdot b_t^{e_t}\equiv 2^{7830457}\pmod m.$
Initially this is true because \(r_0=1\), \(b_0=2\), and \(e_0=7830457\). If \(e_t\) is odd, we move one factor of \(b_t\) from \(b_t^{e_t}\) into \(r_t\); if \(e_t\) is even, we do not need that extra multiplication. In both cases we then replace \(b_t\) by \(b_t^2\) and replace \(e_t\) by \(\lfloor e_t/2\rfloor\), which preserves the same total power of 2 modulo \(m\).
When the loop terminates, \(e_t=0\). The invariant becomes
$r_t\equiv 2^{7830457}\pmod m,$
so the accumulated value is exactly the residue we need.
Worked example with a reduced exponent
A smaller version of the same computation appears naturally as a checkpoint. Replace \(7830457\) by \(20\). Then
$2^{20}=1048576,$
so
$28433\cdot 2^{20}+1=28433\cdot 1048576+1=29814161409.$
Taking the last ten digits gives
$29814161409\bmod 10^{10}=9814161409.$
This is mathematically identical to the full problem. The only difference is that the real exponent is much larger, so repeated squaring is essential.
Why the solution stays with direct modular arithmetic
The modulus is
$10^{10}=2^{10}\cdot 5^{10}.$
Since the base is 2, we have \(\gcd(2,10^{10})=2\neq 1\). That means the usual form of Euler's theorem does not directly reduce the exponent modulo \(\varphi(10^{10})\) for the full modulus. The implementations therefore use the robust approach that always works: repeated squaring with modular reduction at every multiplication.
How the Code Works
Shared computational structure
All three implementations follow the same mathematical pipeline. First they set the modulus to \(10^{10}\). Next they compute the residue of \(2^{7830457}\) modulo that modulus. Then they multiply by \(28433\), add 1, reduce once more modulo \(10^{10}\), and print the resulting ten-digit tail.
Why the arithmetic layer differs by language
The mathematical formula is identical in C++, Python, and Java, but the arithmetic tools are different. The C++ implementation performs modular multiplication through a wider intermediate integer type, because the product of two residues below \(10^{10}\) can be as large as roughly \(10^{20}\), which does not fit safely in ordinary 64-bit multiplication. The Python implementation relies on built-in arbitrary-precision integers and built-in modular exponentiation. The Java implementation uses arbitrary-precision integer arithmetic together with the library operation for modular powers.
Sanity checks in the implementation
The C++ version also verifies the core arithmetic on two smaller cases before printing the final answer. One checkpoint confirms
$2^{10}\equiv 24\pmod{1000},$
and another confirms the reduced full-expression example
$28433\cdot 2^{20}+1\equiv 9814161409\pmod{10^{10}}.$
Those checks reflect the same recurrence and the same final formula used for the actual exponent \(7830457\).
Complexity Analysis
Binary exponentiation uses \(O(\log e)\) modular multiplications, where \(e=7830457\). Since \(e\) has only 23 binary digits, the main loop runs only about 23 iterations, with at most one extra modular multiplication in each iteration when the current bit is 1.
The memory usage is \(O(1)\) in the mathematical sense: the algorithm stores only a fixed number of integers such as the current result, the current base, the current exponent, and the modulus. Even in the arbitrary-precision implementations, every value is immediately reduced modulo \(10^{10}\), so the integers remain tiny compared with the full size of \(N\).
Footnotes and References
- Problem page: https://projecteuler.net/problem=97
- Modular arithmetic: Wikipedia - Modular arithmetic
- Modular exponentiation: Wikipedia - Modular exponentiation
- Exponentiation by squaring: Wikipedia - Exponentiation by squaring
- Proth number: Wikipedia - Proth number