All Euler problems
Project Euler

Prime Subset Sums

Let S = {2, 3, 5, 7, 11, ldots} be the set of all primes less than 5000. Find the number of subsets of S whose elements sum to a prime number. Give the last 16 digits (i.e., the answer modulo 10^16).

Source sync Apr 19, 2026
Problem #0249
Level Level 09
Solved By 2,914
Languages C++, Python
Answer 9275262564250418
Length 323 words
dynamic_programmingmodular_arithmeticnumber_theory

Problem Statement

This archive keeps the full statement, math, and original media on the page.

Let \(S = \{2, 3, 5, \dots , 4999\}\) be the set of prime numbers less than \(5000\).

Find the number of subsets of \(S\), the sum of whose elements is a prime number.

Enter the rightmost \(16\) digits as your answer.

Problem 249: Prime Subset Sums

Mathematical Foundation

Theorem 1 (Subset-sum counting via 0/1 knapsack DP). Let p1<p2<<pKp_1 < p_2 < \cdots < p_K be the primes less than 50005000 (where K=669K = 669), and let σ=i=1Kpi=1,548,136\sigma = \sum_{i=1}^{K} p_i = 1{,}548{,}136. Define dp[s]dp[s] as the number of subsets of {p1,,pK}\{p_1, \ldots, p_K\} that sum to exactly ss, reduced modulo M=1016M = 10^{16}. Then the standard 0/1 knapsack recurrence correctly computes dp[s]dp[s] for all 0sσ0 \le s \le \sigma.

Proof. We prove by induction on jj that after processing primes p1,,pjp_1, \ldots, p_j, dp[s]dp[s] equals (mod MM) the number of subsets of {p1,,pj}\{p_1, \ldots, p_j\} summing to ss.

Base case (j=0j = 0): dp[0]=1dp[0] = 1 (empty subset), dp[s]=0dp[s] = 0 for s>0s > 0. Correct.

Inductive step: Assume the claim holds after j1j - 1 primes. When processing pjp_j, for ss from σ\sigma down to pjp_j, we update dp[s]dp[s]+dp[spj]dp[s] \leftarrow dp[s] + dp[s - p_j]. The reverse iteration ensures that the value dp[spj]dp[s - p_j] used is from the (j1)(j-1)-th stage (before pjp_j was available). Thus:

  • dp[s]dp[s] after update == (subsets of {p1,,pj1}\{p_1,\ldots,p_{j-1}\} summing to ss) ++ (subsets of {p1,,pj1}\{p_1,\ldots,p_{j-1}\} summing to spjs - p_j). The first term counts subsets not containing pjp_j; the second counts subsets containing pjp_j (since adding pjp_j to a subset summing to spjs - p_j gives a subset summing to ss). Together, these are exactly the subsets of {p1,,pj}\{p_1,\ldots,p_j\} summing to ss. \square

Lemma 1 (Prime sieve correctness). The Sieve of Eratosthenes correctly identifies all primes up to NN in O(NloglogN)O(N \log \log N) time.

Proof. Classical result. Each composite nNn \le N has a prime factor pNp \le \sqrt{N}, so it is marked during the sieve of pp. No prime is ever marked. \square

Theorem 2 (Final summation). The answer is

answer=2sσs primedp[s]mod1016.\text{answer} = \sum_{\substack{2 \le s \le \sigma \\ s \text{ prime}}} dp[s] \bmod 10^{16}.

Proof. Each subset with prime sum contributes exactly 1 to dp[s]dp[s] for exactly one prime ss. Summing dp[s]dp[s] over prime ss counts each qualifying subset once. \square

Editorial

We collect primes below 5000. We then 0/1 Knapsack DP. Finally, sum over prime target sums. We use dynamic programming over the state space implied by the derivation, apply each admissible transition, and read the answer from the final table entry.

Pseudocode

Sieve primes up to sigma = 1,548,136
Collect primes below 5000
|primes_5000| = 669
0/1 Knapsack DP
Sum over prime target sums

Complexity Analysis

  • Time: The DP performs K×σ=669×1,548,1361.04×109K \times \sigma = 669 \times 1{,}548{,}136 \approx 1.04 \times 10^9 additions modulo 101610^{16}. The sieve takes O(σloglogσ)O(\sigma \log \log \sigma). The final summation is O(σ)O(\sigma). Total: O(Kσ)O(K \cdot \sigma).
  • Space: O(σ)=O(1,548,136)O(\sigma) = O(1{,}548{,}136) for the DP array and sieve, approximately 12 MB for 64-bit entries.

Answer

9275262564250418\boxed{9275262564250418}

Code

Each problem page includes the exact C++ and Python source files from the local archive.

C++ project_euler/problem_249/solution.cpp
#include <bits/stdc++.h>
using namespace std;

int main() {
    const unsigned long long MOD = 10000000000000000ULL; // 10^16

    // Generate all primes below 5000
    const int LIMIT = 5000;
    vector<bool> sieve(LIMIT, true);
    sieve[0] = sieve[1] = false;
    for (int i = 2; i * i < LIMIT; i++)
        if (sieve[i])
            for (int j = i * i; j < LIMIT; j += i)
                sieve[j] = false;

    vector<int> primes;
    for (int i = 2; i < LIMIT; i++)
        if (sieve[i]) primes.push_back(i);

    long long S = 0;
    for (int p : primes) S += p;

    // Sieve up to S for final primality check
    vector<bool> is_prime(S + 1, true);
    is_prime[0] = is_prime[1] = false;
    for (long long i = 2; i * i <= S; i++)
        if (is_prime[i])
            for (long long j = i * i; j <= S; j += i)
                is_prime[j] = false;

    // DP: 0/1 knapsack
    vector<unsigned long long> dp(S + 1, 0);
    dp[0] = 1;

    for (int p : primes) {
        for (long long s = S; s >= p; s--) {
            dp[s] += dp[s - p];
            if (dp[s] >= MOD) dp[s] -= MOD;
        }
    }

    unsigned long long answer = 0;
    for (long long s = 2; s <= S; s++) {
        if (is_prime[s]) {
            answer += dp[s];
            if (answer >= MOD) answer -= MOD;
        }
    }

    cout << answer << endl;
    return 0;
}