Problem 87 — Prime Power Triples

solved Fri, 18 Jul 2025, 20:49 · level 2 · difficulty 7% projecteuler.net github

The smallest number expressible as the sum of a prime square, prime cube, and prime fourth power is $28$. In fact, there are exactly four numbers below fifty that can be expressed in such a way:

$$\begin{align} 28 &= 2^2 + 2^3 + 2^4\\ 33 &= 3^2 + 2^3 + 2^4\\ 49 &= 5^2 + 2^3 + 2^4\\ 47 &= 2^2 + 3^3 + 2^4 \end{align}$$

How many numbers below fifty million can be expressed as the sum of a prime square, prime cube, and prime fourth power?


Problem Analysis

We are asked to count the distinct integers below N = 50,000,000 that can be written as p2 + q3 + r4, where p, q, and r are each prime numbers. The word "distinct" is the crux of the problem: many different triples (p, q, r) can produce the same sum, so we cannot simply multiply together the counts of valid bases - we must deduplicate. The fact that makes this tractable is that the upper bound N tightly constrains how large each prime can be in each role. Since p2 alone must be below N, the largest prime base we could ever need is at most the square root of N, roughly 7071. The cube and fourth-power roles are far more restrictive: r4 < N means r is at most the fourth root of N (about 84), and q3 < N means q is at most the cube root (about 368). The single most demanding constraint is the square term, so one prime list up to sqrt(N) is a superset of every prime we need for all three roles.

The algorithm is a triple nested loop over (fourth-power prime, cube prime, square prime), computing each sum and inserting it into a deduplication structure. Naively this is O(P4 · P3 · P2), where Pk is the number of primes whose k-th power fits under N; by the prime number theorem each Pk is approximately N1/k / ln N. Because the prime lists are sorted in ascending order, we can break out of each inner loop the moment a partial sum exceeds the budget, which prunes the vast majority of iterations. Empirically the number of valid triples is only a few million, so the dominant costs end up being sieve construction and the deduplication structure, both linear in their respective sizes.

Solution Approach

Triple-loop enumeration with sieve-based prime generation (Python and C)

Both files implement the identical algorithm in two languages, so I describe them together and note the language-specific differences where they matter.

Step 1 - Generate primes with the Sieve of Sundaram. I generate all primes up to sqrt(N) using the Sieve of Sundaram. This sieve marks integers of the form i + j + 2ij (for positive integers i, j ≥ 1) in a half-sized array; any position k that remains unmarked corresponds to the odd prime 2k+1, and I prepend 2 by hand. I chose it over the more familiar Sieve of Eratosthenes largely as a variation - both run in similar time and produce the same primes. The single sqrt(N) bound serves all three roles, as argued above.

Step 2 - Pre-compute early-exit thresholds. Before looping I compute max_quadruple_cube = max_num - 4 (reserving room for the smallest square 22 = 4) and max_quadruple = max_quadruple_cube - 8 (reserving room additionally for the smallest cube 23 = 8). These constants become the break conditions at the outer two loop levels, so I exit as soon as further iterations could not possibly yield a valid sum.

Step 3 - Triple nested loop. The outermost loop iterates over fourth powers, the middle over cubes, and the innermost over squares. At each level I test the running partial sum against its threshold and break (not continue) when it is exceeded. This is correct precisely because the prime lists are ascending: once a partial sum overshoots, every later term in that loop overshoots too.

Step 4 - Deduplication. In Python I add each valid sum to a set and return its length. In C I allocate a calloc-zeroed unsigned char array of length max_num used as a presence table, incrementing a counter only the first time a given index is set. The C array gives guaranteed O(1) lookup with no hashing overhead at the cost of about 50 MB of memory; the Python set uses memory proportional only to the number of distinct sums (~1 million) but pays hash-table overhead per insertion.

workbench

Test cases

categoryinputanswer
dev max_num = 50 4
main max_num = 50000000 1097343

Results

solutioncategoryanswerverdict avg seconds cavg seconds py
p0087_s0 dev 4 correct 0.000001 (63) 0.000003 (63)
p0087_s0 main 1097343 correct 0.042765 (63) 0.287488 (63)

Files

Tags & blogs

Blogs