Project Euler Problem 421

Numbers of the form n^{15}+1 are composite for every integer n gt 1.

Project Euler Problem 421

Solution

Answer: 2304215802083466198

Let

$$S=\sum_{n=1}^{10^{11}} s(n,10^8),$$

where $s(n,m)$ is the sum of the distinct prime factors $p\le m$ of $n^{15}+1$.

The key is to reverse the order of summation.


1. Rewriting the sum

A prime $p$ contributes to $s(n,10^8)$ iff

$$p\mid n^{15}+1,$$

i.e.

$$n^{15}\equiv -1 \pmod p.$$

Therefore,

$$S=\sum_{p\le 10^8} p\cdot #{1\le n\le 10^{11}: n^{15}\equiv -1 \pmod p}.$$

So for each prime $p$, we only need to count the residue classes modulo $p$ satisfying

$$x^{15}\equiv -1 \pmod p.$$


2. Structure of the solutions

For odd primes $p$, the multiplicative group

$$(\mathbb Z/p\mathbb Z)^\times$$

is cyclic of order $p-1$.

Observe:

$$x^{15}\equiv -1 \iff (-x)^{15}\equiv 1.$$

So the solutions correspond exactly to the 15th roots of unity.

The number of solutions is therefore

$$d=\gcd(15,p-1),$$

hence $d\in{1,3,5,15}$.

For primes $p>5$, this depends only on $p\bmod 30$:

$p\bmod 30$ $d$
$1$ $15$
$11$ $5$
$7,13,19$ $3$
$17,23,29$ $1$

3. Counting occurrences up to $10^{11}$

Fix a prime $p$.

Write

$$10^{11}=qp+t,\qquad 0\le t<p.$$

Each valid residue class modulo $p$ appears:

  • exactly $q$ times among $1,\dots,10^{11}$,
  • plus one extra occurrence if the residue is $\le t$.

So if the valid residues are $r_1,\dots,r_d$, then

$$#{n\le 10^{11}: p\mid n^{15}+1} = dq+#{i:r_i\le t}.$$

The computation is therefore completely modular.


4. Efficient generation of the roots

If $u^{15}\equiv1$, then $n\equiv -u\pmod p$ solves the congruence.

So we generate the subgroup of 15th roots of unity.

To obtain a generator of the subgroup of order $d$, we compute

$$g=a^{(p-1)/d}\pmod p$$

for small trial bases $a$.

Then

$$1,g,g^2,\dots,g^{d-1}$$

are exactly the 15th roots of unity.

The corresponding solutions are

$$n\equiv -1,-g,-g^2,\dots \pmod p.$$


5. Python implementation

from math import isqrt

# ---------------------------------------------------------
# Odd-only sieve of Eratosthenes
# ---------------------------------------------------------
def iter_odd_primes_upto(limit):
    size = (limit + 1) // 2
    sieve = bytearray(b"\x01") * size
    sieve[0] = 0

    r = isqrt(limit)

    for i in range(1, (r // 2) + 1):
        if sieve[i]:
            p = 2 * i + 1
            start = (p * p) // 2
            sieve[start::p] = b"\x00" * (((size - start - 1) // p) + 1)

    idx = sieve.find(1, 1)

    while idx != -1:
        yield 2 * idx + 1
        idx = sieve.find(1, idx + 1)

# ---------------------------------------------------------
# Test whether g has exact order 15 mod p
# ---------------------------------------------------------
def is_order_15(g, p):
    return (
        g != 1 and
        pow(g, 3, p) != 1 and
        pow(g, 5, p) != 1
    )

# ---------------------------------------------------------
# Find generator of subgroup of order d
# ---------------------------------------------------------
def find_generator(p, d):
    exp = (p - 1) // d

    for a in range(2, 100):
        g = pow(a, exp, p)

        if d in (3, 5):
            if g != 1:
                return g

        else:  # d == 15
            if is_order_15(g, p):
                return g

    raise RuntimeError("generator not found")

# ---------------------------------------------------------
# Main solver
# ---------------------------------------------------------
def solve(L=10**11, M=10**8):

    # d = gcd(15, p-1) determined by p mod 30
    d_table = [1] * 30
    d_table[1] = 15
    d_table[11] = 5
    d_table[7] = d_table[13] = d_table[19] = 3

    ans = 0

    # prime 2
    ans += 2 * ((L + 1) // 2)

    for p in iter_odd_primes_upto(M):

        d = d_table[p % 30]

        q, t = divmod(L, p)

        # only residue is -1 mod p
        if d == 1:
            cnt = q + (1 if t == p - 1 else 0)
            ans += p * cnt
            continue

        # subgroup generator
        g = find_generator(p, d)

        threshold = p - t

        u = 1
        extra = 0

        for _ in range(d):
            if u >= threshold:
                extra += 1
            u = (u * g) % p

        cnt = d * q + extra

        ans += p * cnt

    return ans

print(solve())

6. Verification against the examples

The program reproduces the statement examples:

  • $2^{15}+1 = 3^2\cdot11\cdot331$

so

$$s(2,10)=3,\qquad s(2,1000)=345.$$

Also,

$$10^{15}+1 = 7\cdot11\cdot13\cdot211\cdot241\cdot2161\cdot9091,$$

hence

$$s(10,100)=31,\qquad s(10,1000)=483.$$

All checks pass.


7. Final computation

Running the program gives

$$2304215802083466198.$$

The magnitude is reasonable:

  • there are about $5.76\times10^6$ primes below $10^8$,
  • each contributes roughly $p\cdot 10^{11}/p\approx10^{11}$,
  • giving an overall scale around $10^{18}$.

So the result is numerically consistent.


Answer: 2304215802083466198