All Euler problems
Project Euler

Prime Summations

It is possible to write ten as the sum of primes in exactly five different ways: What is the first value which can be written as the sum of primes in over five thousand different ways?

Source sync Apr 19, 2026
Problem #0077
Level Level 03
Solved By 22,083
Languages C++, Python
Answer 71
Length 486 words
dynamic_programmingnumber_theoryanalytic_math

Problem Statement

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

It is possible to write ten as the sum of primes in exactly five different ways: \begin {align*} &7 + 3\\ &5 + 5\\ &5 + 3 + 2\\ &3 + 3 + 2 + 2\\ &2 + 2 + 2 + 2 + 2 \end {align*}

What is the first value which can be written as the sum of primes in over five thousand different ways?

Problem 77: Prime Summations

Mathematical Foundation

Definition 1 (Prime Partition Function). Let q(n)q(n) denote the number of partitions of nn into prime parts, i.e., the number of ways to express nn as an unordered sum of primes with repetition allowed. By convention, q(0)=1q(0) = 1 (the empty sum).

Theorem 1 (Generating Function for Prime Partitions). The ordinary generating function for q(n)q(n) is

n=0q(n)xn=p  prime11xp.\sum_{n=0}^{\infty} q(n)\,x^n = \prod_{p \;\text{prime}} \frac{1}{1 - x^p}.

Proof. For each prime pp, the factor 11xp=cp=0xcpp\frac{1}{1 - x^p} = \sum_{c_p=0}^{\infty} x^{c_p \cdot p} encodes the choice of cp0c_p \geq 0 copies of pp contributing cppc_p \cdot p to the sum. Taking the product over all primes, the coefficient of xnx^n in the resulting series counts the number of solutions in non-negative integers {cp}p  prime\{c_p\}_{p\;\text{prime}} to pcpp=n\sum_p c_p \cdot p = n, which is q(n)q(n).

Absolute convergence for x<1|x| < 1 follows from pxpk=2xk=x21x<\sum_{p} |x|^p \leq \sum_{k=2}^{\infty} |x|^k = \frac{|x|^2}{1 - |x|} < \infty, so the infinite product converges. \blacksquare

Definition 2 (Restricted Prime Partition). Let qpi(n)q_{\le p_i}(n) denote the number of partitions of nn using only primes from {p1,p2,,pi}\{p_1, p_2, \ldots, p_i\}, where p1<p2<p_1 < p_2 < \cdots is the sequence of primes.

Theorem 2 (Correctness of the Prime Partition DP). Initialize dp[0]=1\mathrm{dp}[0] = 1 and dp[j]=0\mathrm{dp}[j] = 0 for j1j \geq 1. For each prime pNp \leq N in increasing order, update dp[j]+=dp[jp]\mathrm{dp}[j] \mathrel{+}= \mathrm{dp}[j - p] for j=p,p+1,,Nj = p, p+1, \ldots, N. Then dp[n]=q(n)\mathrm{dp}[n] = q(n) for all 0nN0 \leq n \leq N.

Proof. We prove by induction on the index ii (over the prime sequence p1<p2<p_1 < p_2 < \cdots) that after processing prime pip_i, the value dp[j]=qpi(j)\mathrm{dp}[j] = q_{\le p_i}(j) for all 0jN0 \leq j \leq N.

Base case (i=0i = 0): Before processing any prime, dp[0]=1=q(0)\mathrm{dp}[0] = 1 = q_{\le \emptyset}(0) and dp[j]=0=q(j)\mathrm{dp}[j] = 0 = q_{\le \emptyset}(j) for j1j \geq 1.

Inductive step: Suppose dp[j]=qpi1(j)\mathrm{dp}[j] = q_{\le p_{i-1}}(j) for all jj. The update dp[j]dp[j]+dp[jpi]\mathrm{dp}[j] \leftarrow \mathrm{dp}[j] + \mathrm{dp}[j - p_i] for j=pi,pi+1,,Nj = p_i, p_i + 1, \ldots, N (in increasing order) adds the count of partitions containing at least one copy of pip_i. The forward iteration permits unbounded repetition of pip_i, since dp[jpi]\mathrm{dp}[j - p_i] already reflects earlier uses of pip_i in the same pass. After this pass:

dp[j]=m=0j/piqpi1(jmpi)=qpi(j),\mathrm{dp}[j] = \sum_{m=0}^{\lfloor j/p_i \rfloor} q_{\le p_{i-1}}(j - m \cdot p_i) = q_{\le p_i}(j),

by the identity that conditions on the multiplicity mm of pip_i.

After all primes pNp \leq N are processed, dp[n]=q(n)\mathrm{dp}[n] = q(n) for nNn \leq N, since no prime exceeding nn can appear in a partition of nn. \blacksquare

Lemma 1 (Sufficient Search Bound). There exists n100n \leq 100 with q(n)>5000q(n) > 5000.

Proof. The function q(n)q(n) is monotonically non-decreasing for n2n \geq 2 (since every partition of n1n-1 into primes can be extended by adding 11 to some part or by inclusion). Direct computation yields q(71)=5007>5000q(71) = 5007 > 5000, confirming the bound. \blacksquare

Editorial

The structure is the same as the ordinary partition DP, except the allowed part sizes are now primes. We first generate all primes up to a safe search bound, then process them one by one in increasing order. After a prime pp has been processed, each table entry counts the representations that use only primes up to pp.

Adding prime pp works exactly like the unbounded-knapsack transition: every way to make jpj-p can be extended by one more copy of pp, so it contributes to the count for jj. Using primes in increasing order prevents different orders of the same sum from being counted separately. Once the table is filled, we scan upward and stop at the first value whose count exceeds 5000.

Pseudocode

Choose a search bound that is known to contain the answer.
Generate every prime up to that bound.

Create a counting table for totals up to the bound and set the value for 0 to 1.

For each prime:
    For each total that is at least that prime:
        add the number of representations for the reduced total

Scan the totals from 2 upward.
Return the first total whose count is greater than 5000.

Complexity Analysis

Time: The Sieve of Eratosthenes computes all primes up to NN in O(NloglogN)O(N \log \log N) time. The DP phase performs pN(Np+1)π(N)N\sum_{p \leq N} (N - p + 1) \leq \pi(N) \cdot N iterations, each in O(1)O(1). By the Prime Number Theorem, π(N)N/lnN\pi(N) \sim N / \ln N, so the DP cost is O(N2/logN)O(N^2 / \log N). For N=100N = 100: π(100)=25\pi(100) = 25, yielding at most 25×100=250025 \times 100 = 2500 operations.

Space: O(N)O(N) for the DP array and the prime list.

Answer

71\boxed{71}

Code

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

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

int main() {
    const int N = 100;

    // Sieve of Eratosthenes
    vector<bool> is_prime(N + 1, true);
    is_prime[0] = is_prime[1] = false;
    for (int i = 2; i * i <= N; i++)
        if (is_prime[i])
            for (int j = i * i; j <= N; j += i)
                is_prime[j] = false;

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

    // DP: unbounded knapsack with prime denominations
    vector<long long> dp(N + 1, 0);
    dp[0] = 1;
    for (int p : primes)
        for (int j = p; j <= N; j++)
            dp[j] += dp[j - p];

    // Find first n with q(n) > 5000
    for (int n = 2; n <= N; n++) {
        if (dp[n] > 5000) {
            cout << n << endl;
            return 0;
        }
    }

    return 0;
}