All Euler problems
Project Euler

Prime Pair Sets

Define two primes p and q to be a prime pair if both concatenations p \| q and q \| p are themselves prime. Find the smallest sum of a set of five primes in which every pair of distinct elements fo...

Source sync Apr 19, 2026
Problem #0060
Level Level 02
Solved By 30,940
Languages C++, Python
Answer 26033
Length 757 words
number_theorygraphmodular_arithmetic

Problem Statement

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

The primes \(3\), \(7\), \(109\), and \(673\), are quite remarkable. By taking any two primes and concatenating them in any order the result will always be prime. For example, taking \(7\) and \(109\), both \(7109\) and \(1097\) are prime. The sum of these four primes, \(792\), represents the lowest sum for a set of four primes with this property.

Find the lowest sum for a set of five primes for which any two primes concatenate to produce another prime.

Problem 60: Prime Pair Sets

Mathematical Development

Definition 1 (Concatenation). For positive integers aa and bb, define the concatenation

ab=a10d(b)+b,a \| b = a \cdot 10^{d(b)} + b,

where d(b)=log10b+1d(b) = \lfloor \log_{10} b \rfloor + 1 is the number of decimal digits of bb.

Definition 2 (Prime Pair). Primes pp and qq form a prime pair, written pqp \sim q, if both pqp \| q and qpq \| p are prime.

Definition 3 (Prime Pair Set). A set SS of primes is a prime pair set of size kk if every pair of distinct elements in SS satisfies the prime pair relation. Equivalently, SS is a clique of size kk in the prime pair graph.

Definition 4 (Prime Pair Graph). The prime pair graph is the undirected graph G=(V,E)G = (V, E) where VV is a set of primes and {p,q}E\{p, q\} \in E if and only if pqp \sim q.

Theorem 1 (Modular Necessary Condition). Let p,q>3p, q > 3 be distinct primes. If p≢q(mod3)p \not\equiv q \pmod{3}, then p≁qp \not\sim q.

Proof. Since 101(mod3)10 \equiv 1 \pmod{3}, we have 10d1(mod3)10^d \equiv 1 \pmod{3} for all d1d \geq 1. Therefore

pq=p10d(q)+qp+q(mod3).p \| q = p \cdot 10^{d(q)} + q \equiv p + q \pmod{3}.

For primes p,q>3p, q > 3, we have p,q≢0(mod3)p, q \not\equiv 0 \pmod{3}, so p{1,2}(mod3)p \in \{1, 2\} \pmod{3} and likewise for qq. If p1p \equiv 1 and q2q \equiv 2 (or vice versa), then p+q0(mod3)p + q \equiv 0 \pmod{3}. Since pq>3p \| q > 3, divisibility by 3 implies pqp \| q is composite. By the same argument, qpq \| p is also composite. Hence p≁qp \not\sim q. \square

Corollary 1 (Residue Class Constraint). In any prime pair set of size 2\geq 2 consisting entirely of primes >3> 3, all elements share the same residue modulo 3.

Proof. Immediate from Theorem 1 applied to each pair. \square

Lemma 1 (The Prime 3 is Special). The prime 3 can form a prime pair with primes of either residue class modulo 3. Specifically, for p>3p > 3: 3+pp(mod3)3 + p \equiv p \pmod{3}, which is nonzero since p≢0(mod3)p \not\equiv 0 \pmod{3}. Hence the mod-3 obstruction does not apply to pairs involving 3.

Proof. 3p=310d(p)+p3+pp(mod3)≢03 \| p = 3 \cdot 10^{d(p)} + p \equiv 3 + p \equiv p \pmod{3} \not\equiv 0. Similarly, p3=10p+3p+3p(mod3)≢0p \| 3 = 10p + 3 \equiv p + 3 \equiv p \pmod{3} \not\equiv 0. So neither concatenation is divisible by 3 (though they may still be composite for other reasons). \square

Theorem 2 (Graph-Theoretic Reduction). Finding a prime pair set of size 5 with minimum sum is equivalent to finding a minimum-weight 5-clique in the prime pair graph GG.

Proof. By Definition 3, a prime pair set of size 5 is a 5-clique in GG. The sum of the five primes is the weight. Minimizing over all 5-cliques gives the answer. \square

Lemma 2 (Search Bound). It suffices to search primes below some bound BB. If a 5-clique with sum Σ\Sigma is found, then B=ΣB = \Sigma suffices, since any clique containing a prime >Σ> \Sigma has sum >Σ> \Sigma.

Proof. Each prime in the clique is at most Σ\Sigma (since all five are positive and sum to Σ\Sigma). More precisely, the largest prime is at most Σ42=Σ8\Sigma - 4 \cdot 2 = \Sigma - 8 (since the smallest prime is 2). In practice, B=10,000B = 10{,}000 is sufficient as confirmed by computation. \square

Lemma 3 (Primality of Concatenations). For primes below B=10,000B = 10{,}000, all concatenations satisfy pq<108p \| q < 10^8. Deterministic Miller-Rabin primality testing with witness set {2,3,5,7,11,13}\{2, 3, 5, 7, 11, 13\} is correct for all integers below 3.2×10143.2 \times 10^{14} (Jaeschke, 1993), which amply covers our range.

Proof. The maximum concatenation is 99999973<104104+104=108+104<3.2×10149999 \| 9973 < 10^4 \cdot 10^4 + 10^4 = 10^8 + 10^4 < 3.2 \times 10^{14}. The cited result guarantees deterministic correctness. \square

Theorem 3 (Incremental Clique Search with Pruning). The 5-clique search can be organized as a depth-first enumeration with the following pruning:

(i) Ordering: enumerate clique elements in increasing order p1<p2<p3<p4<p5p_1 < p_2 < p_3 < p_4 < p_5.

(ii) Adjacency intersection: at depth \ell, the candidate set for p+1p_{\ell+1} is the intersection of the adjacency lists of p1,,pp_1, \ldots, p_\ell restricted to elements >p> p_\ell.

(iii) Bound pruning: if the current partial sum plus the minimum possible contribution from remaining elements exceeds the best known sum, prune.

(iv) Mod-3 filter: by Corollary 1, restrict candidates at depth 2\geq 2 to primes sharing the residue class of p1p_1 modulo 3 (unless p1=3p_1 = 3).

Proof of correctness. (i) avoids redundant permutations. (ii) ensures every pair in the clique is adjacent. (iii) is a standard branch-and-bound argument. (iv) follows from Theorem 1 and Lemma 1. \square

Editorial

We first generate the primes below a search bound and build the prime pair graph whose edges correspond to pairs of primes whose two concatenations are both prime. The desired set is then a 5-clique of minimum weight in this graph, so we search for it incrementally: a partial clique is extended only by primes adjacent to all of its current vertices, and branch-and-bound inequalities discard any branch whose best possible completion cannot improve the smallest sum found so far.

Pseudocode

Algorithm: Minimum-sum Prime Pair 5-clique
Require: A prime bound B and a primality test for concatenations below the induced search range.
Ensure: The smallest sum of five primes for which every pair forms a prime pair.
1: Generate the primes below B and build the prime pair graph by connecting distinct primes p and q whenever both p || q and q || p are prime.
2: Search the graph for 5-cliques in increasing prime order, extending each partial clique only by common neighbors and pruning any branch whose optimistic completion already exceeds the best sum found.
3: Return the minimum clique sum obtained.

Complexity Analysis

Theorem 4 (Pair Precomputation Cost). Let P=π(B)P = \pi(B) denote the number of primes below BB (P=1229P = 1229 for B=10,000B = 10{,}000). The adjacency list construction requires (P2)=O(P2)\binom{P}{2} = O(P^2) pair tests, each costing O(log2B)O(\log^2 B) with Miller-Rabin. Total: O(P2log2B)O(P^2 \log^2 B).

Proof. There are (12292)=754,506\binom{1229}{2} = 754{,}506 pairs. Each pair test involves two concatenations and two Miller-Rabin primality tests on numbers up to 10810^8. Each Miller-Rabin test with a constant number of witnesses costs O(log2n)=O(log2B)O(\log^2 n) = O(\log^2 B). \square

Theorem 5 (Clique Search Cost). The worst-case search is O(P5)O(P^5), but the pruning from adjacency intersection and bound checks reduces the practical cost to far below this. The dominant cost is the O(P2)O(P^2) pair precomputation.

Space: O(P2)O(P^2) for the full adjacency structure, or O(Pdˉ)O(P \cdot \bar{d}) using adjacency lists where dˉ\bar{d} is the average degree.

Answer

26033\boxed{26033}

Code

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

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

typedef long long ll;

// Sieve of Eratosthenes
vector<bool> sieve;
vector<int> primes;

void buildSieve(int n) {
    sieve.assign(n + 1, true);
    sieve[0] = sieve[1] = false;
    for (int i = 2; i * i <= n; i++) {
        if (sieve[i]) {
            for (int j = i * i; j <= n; j += i)
                sieve[j] = false;
        }
    }
    for (int i = 2; i <= n; i++)
        if (sieve[i]) primes.push_back(i);
}

// Modular multiplication via __int128 to avoid overflow.
ll mulmod(ll a, ll b, ll m) {
    return (__int128)a * b % m;
}

// Fast modular exponentiation.
ll powmod(ll base, ll exp, ll mod) {
    ll result = 1;
    base %= mod;
    while (exp > 0) {
        if (exp & 1) result = mulmod(result, base, mod);
        base = mulmod(base, base, mod);
        exp >>= 1;
    }
    return result;
}

// Single round of Miller-Rabin with witness a.
bool millerRabin(ll n, ll a) {
    if (n % a == 0) return n == a;
    ll d = n - 1;
    int r = 0;
    while (d % 2 == 0) { d /= 2; r++; }
    ll x = powmod(a, d, n);
    if (x == 1 || x == n - 1) return true;
    for (int i = 0; i < r - 1; i++) {
        x = mulmod(x, x, n);
        if (x == n - 1) return true;
    }
    return false;
}

// Deterministic primality test.
bool isPrime(ll n) {
    if (n < (int)sieve.size()) return sieve[n];
    if (n < 2) return false;
    if (n % 2 == 0 || n % 3 == 0) return false;
    for (ll a : {2, 3, 5, 7, 11, 13}) {
        if (!millerRabin(n, a)) return false;
    }
    return true;
}

// Concatenate a and b: returns a || b = a * 10^{d(b)} + b.
ll concat(ll a, ll b) {
    ll tmp = b, mult = 1;
    while (tmp > 0) { mult *= 10; tmp /= 10; }
    return a * mult + b;
}

// Check if primes a and b form a prime pair.
bool pairCheck(int a, int b) {
    return isPrime(concat(a, b)) && isPrime(concat(b, a));
}

int main() {
    const int LIMIT = 10000;
    buildSieve(LIMIT * 100);  // sieve large enough for concatenations

    int n = primes.size();
    int bound = upper_bound(primes.begin(), primes.end(), LIMIT) - primes.begin();

    // Build adjacency lists
    vector<vector<int>> adj(bound);
    for (int i = 0; i < bound; i++) {
        for (int j = i + 1; j < bound; j++) {
            if (pairCheck(primes[i], primes[j])) {
                adj[i].push_back(j);
            }
        }
    }

    // Fast pair lookup via set
    set<pair<int,int>> pairSet;
    for (int i = 0; i < bound; i++) {
        for (int j : adj[i]) {
            pairSet.insert({i, j});
        }
    }

    auto paired = [&](int i, int j) -> bool {
        if (i > j) swap(i, j);
        return pairSet.count({i, j});
    };

    int bestSum = INT_MAX;

    // Depth-first 5-clique search
    for (int i = 0; i < bound; i++) {
        for (int j : adj[i]) {
            for (int k : adj[j]) {
                if (k <= j) continue;
                if (!paired(i, k)) continue;
                for (int l : adj[k]) {
                    if (l <= k) continue;
                    if (!paired(i, l) || !paired(j, l)) continue;
                    for (int m : adj[l]) {
                        if (m <= l) continue;
                        if (!paired(i, m) || !paired(j, m) || !paired(k, m)) continue;
                        int s = primes[i] + primes[j] + primes[k] + primes[l] + primes[m];
                        bestSum = min(bestSum, s);
                    }
                }
            }
        }
    }

    cout << bestSum << endl;
    return 0;
}