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

Project Euler Problem 619

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