Problem 97 — Large Non-Mersenne Prime

solved Sat, 19 Jul 2025, 04:14 · level 1 · difficulty 4% projecteuler.net github

The first known prime found to exceed one million digits was discovered in 1999, and is a Mersenne prime of the form $2^{6972593} - 1$; it contains exactly $2\,098\,960$ digits. Subsequently other Mersenne primes, of the form $2^p - 1$, have been found which contain more digits.

However, in 2004 there was found a massive non-Mersenne prime which contains $2\,357\,207$ digits: $28433 \times 2^{7830457} + 1$.

Find the last ten digits of this prime number.


Problem Analysis

We are asked for the last ten digits of the integer 28433 × 2^7830457 + 1. The single observation that makes this tractable is recognizing that "the last ten digits" of any integer is precisely that integer reduced modulo 10^10. The full number has over two million digits, far too large to write out directly, but we never need it - we only need its residue modulo 10^10. This reframing converts an intractable big-integer computation into a sequence of operations on numbers below 10^10.

The reason the reframing works is that modular arithmetic is closed under addition and multiplication: (a × b) mod m = ((a mod m) × (b mod m)) mod m, and likewise for sums. This lets us reduce after every step so that no intermediate value ever escapes its residue. The one remaining hurdle is the exponent 7830457. Multiplying by 2 that many times would be O(exp) - around 7.8 million operations. The standard cure is modular exponentiation via exponentiation by squaring, which decomposes the exponent into its binary digits and squares the base repeatedly, computing a^b mod m in O(log b) multiplications - about 23 here instead of millions. The overall complexity is O(log exp), effectively instantaneous.

Solution Approach

Doubling Loop (Python)

The Python solution reframes the problem correctly - it computes divisor = 10^num_digits, parses the coefficient and exponent out of the expression string, and reduces modulo divisor after each operation - but it computes the power the slow way. Its loop runs exponent times (7,830,457 iterations), each time doubling the running value and reducing modulo divisor. This is mathematically valid because modular arithmetic is closed under multiplication, but it is O(exp) rather than O(log exp). Because Python integers have arbitrary precision, there is no overflow concern, so the reduction is purely to keep the numbers small and the per-iteration cost constant. After the loop it adds 1 and reduces once more. The natural improvement would be to replace the loop with Python's built-in three-argument pow(2, exponent, divisor), which performs fast modular exponentiation in C internally; correctness would be identical and the speed would collapse to the C version's.

Square-and-Multiply Modular Exponentiation (C)

The C solution implements the asymptotically efficient route. The heart of it is mod_pow, the classic exponentiation by squaring loop. While the exponent is nonzero, if its lowest bit is set we multiply the running result by the current base (reducing mod m), then we square the base and shift the exponent right by one bit. Each iteration consumes exactly one bit of the exponent, so the loop runs about log2(7830457) ≈ 23 times instead of 7.8 million. After computing power = 2^exponent mod divisor, it forms result = (coefficient × power + 1) mod divisor, reducing the coefficient first to keep operands small.

A subtle correctness point lurks in the multiplications. When two values each approach 10^10 are multiplied, the product can reach nearly 10^20, beyond the roughly 9.2 × 10^18 range of a signed 64-bit integer. To avoid this overflow I cast both operands to __int128 before multiplying, reduce modulo mod, then cast back to long long. The expression itself is parsed by concatenating the argv tokens back into one string (the shell splits "28433 × 2^7830457 + 1" into separate arguments), reading the leading coefficient with atoll, and locating the exponent after the ^ with strchr.

workbench

Test cases

categoryinputanswer
main num_digits = 10 prime = 28433 × 2^7830457 + 1 8739992577

Results

solutioncategoryanswerverdict avg seconds cavg seconds py
p0097_s0 main 8739992577 correct 0.000001 (63) 0.559610 (63)

Files

Tags & blogs

Blogs