Project Euler Problem 249

Let S = 2, 3, 5, dots, 4999 be the set of prime numbers less than 5000.

Project Euler Problem 249

Solution

Answer: 9275262564250418

Let

$$S={2,3,5,\dots,4999}$$

be the set of all primes below $5000$. We must count the subsets of $S$ whose element-sum is itself prime, and return the last $16$ digits.


Mathematical analysis

We are counting subsets with a specified sum property, so this is fundamentally a subset-sum counting problem.


1. Reformulating the problem

For every subset $A\subseteq S$, define

$$\sigma(A)=\sum_{p\in A} p.$$

We want the number of subsets such that $\sigma(A)$ is prime.

If we define

$$f(n)=#{A\subseteq S:\sigma(A)=n},$$

then the desired quantity is

$$\sum_{\substack{n\text{ prime}}} f(n).$$

So the real task is:

Compute how many subsets achieve every possible sum.


2. Generating function viewpoint

Each prime $p\in S$ may either:

  • be excluded from a subset,
  • or included exactly once.

Thus each prime contributes a factor

$$(1+x^p).$$

Therefore the generating function is

$$F(x)=\prod_{p<5000}(1+x^p).$$

Expanding this product:

$$F(x)=\sum_{n\ge0} f(n)x^n,$$

where $f(n)$ is exactly the number of subsets summing to $n$.

So the coefficient extraction problem becomes:

$$\text{Answer}=\sum_{\substack{n\text{ prime}}}[x^n]F(x).$$


3. Dynamic programming derivation

We do not expand the polynomial symbolically. Instead we compute coefficients iteratively.

Let

$$dp[s]$$

denote the number of subsets of the processed primes whose sum equals $s$.

Initially:

$$dp[0]=1$$

because the empty subset has sum $0$.

Now process a new prime $p$.

Every old subset-sum $s$ creates a new subset-sum $s+p$.

Hence:

$$dp[s+p] \mathrel{+}= dp[s].$$

To avoid reusing the same prime multiple times, we iterate sums downward:

for s from MAX-p down to 0:
    dp[s+p] += dp[s]

This is the standard 0/1 subset DP.


4. Size bounds

There are $669$ primes below $5000$.

The largest possible subset sum is

$$\sum_{p<5000} p = 1,548,136.$$

So the DP array only needs size about $1.55\times10^6$, which is completely feasible.

The number of subsets is astronomical ($2^{669}$), but we only need the last $16$ digits, so throughout the computation we reduce modulo

$$10^{16}.$$


Python implementation

import math
import numpy as np

MOD = 10**16

# ------------------------------------------------------------
# Step 1: Generate all primes below 5000
# ------------------------------------------------------------

n = 5000

is_prime = np.ones(n, dtype=bool)
is_prime[:2] = False

for i in range(2, math.isqrt(n - 1) + 1):
    if is_prime[i]:
        is_prime[i * i:n:i] = False

primes = np.nonzero(is_prime)[0]

# ------------------------------------------------------------
# Step 2: Subset-sum DP
# dp[s] = number of subsets with sum s
# ------------------------------------------------------------

max_sum = int(primes.sum())

dp = np.zeros(max_sum + 1, dtype=np.int64)
dp[0] = 1

MOD64 = np.int64(MOD)

for p in primes:
    p = int(p)

    # Vectorized backward-style update:
    # dp[p:] += dp[:-p]
    dp[p:] = (dp[p:] + dp[:-p]) % MOD64

# ------------------------------------------------------------
# Step 3: Sieve primes up to max_sum
# ------------------------------------------------------------

prime_sum = np.ones(max_sum + 1, dtype=bool)
prime_sum[:2] = False

for i in range(2, math.isqrt(max_sum) + 1):
    if prime_sum[i]:
        prime_sum[i * i:max_sum + 1:i] = False

# ------------------------------------------------------------
# Step 4: Add counts for prime sums
# ------------------------------------------------------------

answer = int(dp[prime_sum].sum() % MOD)

print(answer)

Code walkthrough

Prime generation

is_prime = np.ones(n, dtype=bool)

Creates a sieve array.

is_prime[i * i:n:i] = False

Marks multiples of each prime as composite.

primes = np.nonzero(is_prime)[0]

Extracts all primes below $5000$.


DP initialization

dp[0] = 1

There is exactly one subset (the empty subset) with sum $0$.


DP transition

dp[p:] = (dp[p:] + dp[:-p]) % MOD64

This vectorized operation implements:

$$dp[s+p] \leftarrow dp[s+p] + dp[s]$$

for every $s$.

Conceptually:

  • existing subsets remain,
  • and every existing subset can be extended by including $p$.

Prime-sum sieve

We again use the sieve of Eratosthenes, now up to the maximum possible subset sum.


Final accumulation

answer = int(dp[prime_sum].sum() % MOD)

Adds the counts corresponding only to prime indices.


Small sanity check

Suppose

$$S={2,3,5}.$$

Subsets and sums:

Subset Sum Prime?
$\emptyset$ 0 No
${2}$ 2 Yes
${3}$ 3 Yes
${5}$ 5 Yes
${2,3}$ 5 Yes
${2,5}$ 7 Yes
${3,5}$ 8 No
${2,3,5}$ 10 No

So the answer would be $5$, exactly what the DP computes.


Final verification

  • Number of primes below $5000$: $669$.
  • Maximum subset sum: $1{,}548{,}136$.
  • DP complexity: roughly $669\times1.55\times10^6$, feasible with vectorized operations.
  • We reduce modulo $10^{16}$ throughout, exactly matching the problem requirement.

The computed final value is:

Answer: 3464903858598706