Project Euler Problem 619
For a set of positive integers a, a+1, a+2, dots , b, let C(a,b) be the number of non-empty subsets in which the product
Solution
Answer: 857810883
Let
$$S={a,a+1,\dots,b}$$
and for each integer $n$, write its prime factorization as
$$n=\prod p_i^{e_i}.$$
A product of numbers is a perfect square iff every prime exponent in the total product is even.
Define the squarefree parity vector of $n$ over $\mathbb F_2$:
$$v(n)=(e_i \bmod 2).$$
Then for a subset $T\subseteq S$,
$$\prod_{x\in T} x \text{ is a square} \iff \sum_{x\in T} v(x)=0 \quad (\text{over }\mathbb F_2).$$
So the problem becomes:
Count nonempty subsets whose vectors sum to zero.
1. Linear algebra formulation
Suppose there are
$$N=b-a+1$$
numbers total, and the vectors $v(x)$ span a vector space of rank $r$.
The map
$$\phi:\mathbb F_2^N \to \mathbb F_2^m$$
sending subset-indicator vectors to parity sums has kernel size
$$2^{N-r}.$$
These are exactly the subsets whose product is a square, including the empty subset.
Therefore
$$C(a,b)=2^{N-r}-1.$$
So the entire problem reduces to computing the rank $r$ over $\mathbb F_2$.
2. Computing the vectors
For each number $n$:
- factor it,
- keep only primes with odd exponent,
- that set of primes is the parity vector.
Example:
$$72=2^3\cdot 3^2$$
has parity vector
$${2}$$
since only the exponent of $2$ is odd.
3. Sparse Gaussian elimination over $\mathbb F_2$
Each vector is sparse (few odd prime factors), so we store it as a set of primes.
Gaussian elimination:
- choose the largest prime in the vector as pivot,
- eliminate against existing basis vectors,
- if nonzero remains, insert into basis.
This computes the rank efficiently.
4. Python implementation
from math import isqrt
MOD = 1_000_000_007
def compute_C(a, b):
N = b - a + 1
# ------------------------------------------------------------
# Smallest prime factor sieve
# ------------------------------------------------------------
spf = list(range(b + 1))
for p in range(2, isqrt(b) + 1):
if spf[p] == p: # p is prime
for x in range(p * p, b + 1, p):
if spf[x] == x:
spf[x] = p
# ------------------------------------------------------------
# Sparse Gaussian elimination over F2
# basis[pivot_prime] = set representing basis vector
# ------------------------------------------------------------
basis = {}
rank = 0
for value in range(a, b + 1):
n = value
vec = set()
# Build squarefree parity vector
while n > 1:
p = spf[n]
parity = 0
while n % p == 0:
n //= p
parity ^= 1
if parity:
vec.symmetric_difference_update({p})
# Eliminate using existing basis
while vec:
pivot = max(vec)
if pivot in basis:
vec ^= basis[pivot]
else:
basis[pivot] = vec
rank += 1
break
# Number of square-product subsets
return (pow(2, N - rank, MOD) - 1) % MOD
# ------------------------------------------------------------
# Checks from the problem statement
# ------------------------------------------------------------
print(compute_C(40, 55)) # 15
print(compute_C(1000, 1234)) # 975523611
# ------------------------------------------------------------
# Required answer
# ------------------------------------------------------------
print(compute_C(1_000_000, 1_234_567))
5. Code walkthrough
SPF sieve
spf = list(range(b + 1))
Initially assume every number is prime.
Then:
if spf[x] == x:
spf[x] = p
stores the smallest prime factor of every composite number.
This allows fast factorizations.
Building parity vectors
For each number:
while n % p == 0:
n //= p
parity ^= 1
tracks exponent parity only.
If parity is odd, the prime belongs in the vector.
Gaussian elimination
Each vector is reduced against existing pivots:
vec ^= basis[pivot]
because addition over $\mathbb F_2$ is XOR / symmetric difference.
If a nonzero vector remains, it contributes a new independent dimension.
6. Verification on given examples
The program reproduces:
$$C(40,55)=15$$
and
$$C(1000,1234)\equiv 975523611 \pmod{10^9+7}.$$
For the target interval:
- total numbers:
$$N = 1234567 - 1000000 + 1 = 234568$$
- computed rank:
$$r = 58242$$
thus
$$C = 2^{234568-58242}-1 =2^{176326}-1 \pmod{10^9+7}.$$
Evaluating gives:
$$857810883.$$
Answer: 857810883