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...
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 and , define the concatenation
where is the number of decimal digits of .
Definition 2 (Prime Pair). Primes and form a prime pair, written , if both and are prime.
Definition 3 (Prime Pair Set). A set of primes is a prime pair set of size if every pair of distinct elements in satisfies the prime pair relation. Equivalently, is a clique of size in the prime pair graph.
Definition 4 (Prime Pair Graph). The prime pair graph is the undirected graph where is a set of primes and if and only if .
Theorem 1 (Modular Necessary Condition). Let be distinct primes. If , then .
Proof. Since , we have for all . Therefore
For primes , we have , so and likewise for . If and (or vice versa), then . Since , divisibility by 3 implies is composite. By the same argument, is also composite. Hence .
Corollary 1 (Residue Class Constraint). In any prime pair set of size consisting entirely of primes , all elements share the same residue modulo 3.
Proof. Immediate from Theorem 1 applied to each pair.
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 : , which is nonzero since . Hence the mod-3 obstruction does not apply to pairs involving 3.
Proof. . Similarly, . So neither concatenation is divisible by 3 (though they may still be composite for other reasons).
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 .
Proof. By Definition 3, a prime pair set of size 5 is a 5-clique in . The sum of the five primes is the weight. Minimizing over all 5-cliques gives the answer.
Lemma 2 (Search Bound). It suffices to search primes below some bound . If a 5-clique with sum is found, then suffices, since any clique containing a prime has sum .
Proof. Each prime in the clique is at most (since all five are positive and sum to ). More precisely, the largest prime is at most (since the smallest prime is 2). In practice, is sufficient as confirmed by computation.
Lemma 3 (Primality of Concatenations). For primes below , all concatenations satisfy . Deterministic Miller-Rabin primality testing with witness set is correct for all integers below (Jaeschke, 1993), which amply covers our range.
Proof. The maximum concatenation is . The cited result guarantees deterministic correctness.
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 .
(ii) Adjacency intersection: at depth , the candidate set for is the intersection of the adjacency lists of restricted to elements .
(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 to primes sharing the residue class of modulo 3 (unless ).
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.
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 denote the number of primes below ( for ). The adjacency list construction requires pair tests, each costing with Miller-Rabin. Total: .
Proof. There are pairs. Each pair test involves two concatenations and two Miller-Rabin primality tests on numbers up to . Each Miller-Rabin test with a constant number of witnesses costs .
Theorem 5 (Clique Search Cost). The worst-case search is , but the pruning from adjacency intersection and bound checks reduces the practical cost to far below this. The dominant cost is the pair precomputation.
Space: for the full adjacency structure, or using adjacency lists where is the average degree.
Answer
Code
Each problem page includes the exact C++ and Python source files from the local archive.
#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;
}
"""
Project Euler Problem 60: Prime Pair Sets
Find the lowest sum for a set of five primes for which any two
primes concatenate (in either order) to produce another prime.
Approach: build the prime-pair graph and search for minimum-weight
5-cliques via depth-first enumeration with adjacency-intersection
pruning and branch-and-bound.
Answer: 26033
"""
from sympy import isprime, primerange
def solve():
LIMIT = 10000
primes = list(primerange(2, LIMIT))
pair_cache = {}
def is_pair(a, b):
key = (a, b)
if key not in pair_cache:
pair_cache[key] = (
isprime(int(str(a) + str(b))) and
isprime(int(str(b) + str(a)))
)
return pair_cache[key]
# Build adjacency lists
adj = {p: [] for p in primes}
for i in range(len(primes)):
for j in range(i + 1, len(primes)):
if is_pair(primes[i], primes[j]):
adj[primes[i]].append(primes[j])
best = float('inf')
# Incremental 5-clique search
for p1 in primes:
if p1 * 5 >= best:
break
for p2 in adj[p1]:
if p1 + p2 * 4 >= best:
break
friends2 = [p for p in adj[p1] if p > p2 and is_pair(p2, p)]
for p3 in friends2:
if p1 + p2 + p3 * 3 >= best:
break
friends3 = [p for p in friends2 if p > p3 and is_pair(p3, p)]
for p4 in friends3:
if p1 + p2 + p3 + p4 * 2 >= best:
break
friends4 = [p for p in friends3 if p > p4 and is_pair(p4, p)]
for p5 in friends4:
s = p1 + p2 + p3 + p4 + p5
if s < best:
best = s
return best
if __name__ == "__main__":
answer = solve()
assert answer == 26033
print(answer)