Writing 1/2 as a Sum of Inverse Squares
Find the number of distinct subsets S subseteq {2, 3,..., 80} such that sum_(k in S) (1)/(k^2) = (1)/(2).
Problem Statement
This archive keeps the full statement, math, and original media on the page.
There are several ways to write the number \(\dfrac {1}{2}\) as a sum of square reciprocals using
For instance, the numbers \(\{2,3,4,5,7,12,15,20,28,35\}\) can be used: \begin {align*} \dfrac {1}{2} &= \dfrac {1}{2^2} + \dfrac {1}{3^2} + \dfrac {1}{4^2} + \dfrac {1}{5^2} +\\ &\quad \dfrac {1}{7^2} + \dfrac {1}{12^2} + \dfrac {1}{15^2} + \dfrac {1}{20^2} +\\ &\quad \dfrac {1}{28^2} + \dfrac {1}{35^2} \end {align*}
In fact, only using integers between \(2\) and \(45\) inclusive, there are exactly three ways to do it, the remaining two being: \(\{2,3,4,6,7,9,10,20,28,35,36,45\}\) and \(\{2,3,4,6,7,9,12,15,28,30,35,36,45\}\).
How many ways are there to write \(\dfrac {1}{2}\) as a sum of reciprocals of squares using distinct integers between \(2\) and \(80\) inclusive?
Problem 152: Writing 1/2 as a Sum of Inverse Squares
Mathematical Development
Definition 1. Let . For any subset , define , so the target equation becomes .
Theorem 1 (Large Prime Elimination). Let be a prime with . Then for any valid subset .
Proof. Since , the only multiple of in is itself (as ). Consider the -adic valuation of the cleared equation . We have (from and , since but appears only once in the range). For with , . For , .
Suppose . Then . But (since ). This gives , a contradiction (more precisely, the term with cannot cancel since it is the unique term with minimal -adic valuation). Hence .
This eliminates .
Theorem 2 (Paired Prime Elimination). Let be a prime with , and let . The subset must satisfy a modular constraint: the partial sum must be congruent to a specific residue modulo . For each such , exhaustive enumeration of the (at most ) subsets of determines which elements can appear.
Proof. Write the cleared equation as . Partition . For every , we have , so . Hence . Also since , so .
Therefore . Since for (at most and possibly lie in ), the constraint is verified by exhaustive check over at most subsets. If no non-empty subset of satisfies the congruence, all elements of are excluded from .
Lemma 1 (Reduced Candidate Set). Applying Theorems 1 and 2 systematically for all primes from 79 down to 2, the candidate set reduces from 79 elements to approximately 36 elements, with certain elements forced to appear in specific groups.
Proof. Direct computation of modular constraints for each prime. The eliminated elements include all multiples of primes that fail the congruence test, as well as various multiples of smaller primes constrained by similar -adic arguments.
Theorem 3 (Correctness of DFS Enumeration). The valid subsets are enumerated by depth-first search over the reduced candidate set with integer arithmetic and the following pruning rules:
- If the partial sum exceeds , prune (remaining terms are positive).
- If the partial sum plus the sum of all remaining candidates is less than , prune (insufficient capacity).
The search is exhaustive over the non-pruned branches and therefore returns the exact count.
Proof. Let be the sorted candidates with corresponding values . The DFS considers, for each index , whether to include in . Let . At any node with partial sum at index :
- If : since for all , adding further elements only increases the sum, so no completion exists.
- If : even including all remaining elements cannot reach , so no completion exists.
Both pruning conditions eliminate only infeasible branches. The base case ( or ) is exact. By structural induction on the DFS tree, all valid subsets are found.
Editorial
We prime elimination (reduce candidate set). Finally, integer DFS with pruning. We perform a recursive search over the admissible choices, prune branches that violate the derived constraints, and keep only the candidates that satisfy the final condition.
Pseudocode
Prime elimination (reduce candidate set)
Integer DFS with pruning
Complexity Analysis
- Time: in the worst case where is the candidate count. The branch-and-bound pruning dramatically reduces the effective search space; empirically the search completes in under one second.
- Space: for the recursion stack and for the precomputed suffix sums.
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 double ld;
int main() {
set<int> excluded = {
79,73,71,67,61,59,53,47,43,41,37,74,31,62,29,58,23,46,69,
19,38,57,76,34,51,26,65,78,11,22,33,44,55,66,77,49,25,50,
75,27,54,17,68
};
vector<int> candidates;
for (int k = 2; k <= 80; k++)
if (excluded.find(k) == excluded.end())
candidates.push_back(k);
int n = candidates.size();
ld target = 0.5L;
vector<ld> vals(n);
for (int i = 0; i < n; i++)
vals[i] = 1.0L / ((ld)candidates[i] * candidates[i]);
vector<ld> suffix(n + 1, 0);
for (int i = n - 1; i >= 0; i--)
suffix[i] = suffix[i + 1] + vals[i];
int count = 0;
ld eps = 1e-18L;
function<void(int, ld)> dfs = [&](int idx, ld current) {
if (fabsl(current - target) < eps) { count++; return; }
if (idx >= n) return;
ld need = target - current;
if (need < -eps) return;
if (suffix[idx] < need - eps) return;
for (int i = idx; i < n; i++) {
if (vals[i] > need + eps) continue;
if (suffix[i] < need - eps) return;
dfs(i + 1, current + vals[i]);
}
};
dfs(0, 0.0L);
printf("%d\n", count);
return 0;
}
from fractions import Fraction
from math import gcd
from functools import reduce
def solve():
excluded = {
79, 73, 71, 67, 61, 59, 53, 47, 43, 41, 37, 74, 31, 62, 29, 58,
23, 46, 69, 19, 38, 57, 76, 34, 51, 26, 65, 78, 11, 22, 33, 44,
55, 66, 77, 49, 25, 50, 75, 27, 54, 17, 68
}
candidates = sorted(k for k in range(2, 81) if k not in excluded)
def lcm(a, b):
return a * b // gcd(a, b)
L = reduce(lcm, (k * k for k in candidates))
target = L // 2
vals = [L // (k * k) for k in candidates]
n = len(candidates)
suffix = [0] * (n + 1)
for i in range(n - 1, -1, -1):
suffix[i] = suffix[i + 1] + vals[i]
count = 0
def dfs(idx, current):
nonlocal count
if current == target:
count += 1
return
if idx >= n:
return
need = target - current
if need <= 0 or suffix[idx] < need:
return
for i in range(idx, n):
if vals[i] > need:
continue
if suffix[i] < need:
return
dfs(i + 1, current + vals[i])
dfs(0, 0)
print(count)
solve()