Project Euler Problem 124

The radical of n, operatorname{rad}(n), is the product of the distinct prime factors of n.

Project Euler Problem 124

Solution

Answer: 21417

We need to compute $E(10000)$ when the integers $1 \le n \le 100000$ are sorted by:

  1. Increasing $\operatorname{rad}(n)$,
  2. Breaking ties by increasing $n$.

The radical is defined as the product of the distinct prime factors of $n$.

For example:

  • $504 = 2^3 \cdot 3^2 \cdot 7$,
  • so

$$\operatorname{rad}(504)=2\cdot3\cdot7=42.$$


Mathematical analysis

The direct definition suggests two tasks:

  1. Compute $\operatorname{rad}(n)$ efficiently for all $n \le 100000$,
  2. Sort pairs $(\operatorname{rad}(n), n)$.

A naïve factorization of every number individually would work, but we can do much better with a sieve-like approach.

Key observation

If $p$ is prime, then every multiple of $p$ has $p$ as a distinct prime factor.

So we can initialize:

$$\operatorname{rad}(n)=1$$

for all $n$, and then for every prime $p$:

  • multiply $\operatorname{rad}(k)$ by $p$ for every multiple $k$ of $p$.

This is analogous to the Sieve of Eratosthenes.


Example

Start with:

$$\operatorname{rad}(1)=\operatorname{rad}(2)=\cdots=1.$$

Process prime $2$:

  • multiply all multiples of 2 by 2.

Now:

$$\operatorname{rad}(2)=2,\quad \operatorname{rad}(4)=2,\quad \operatorname{rad}(6)=2,\dots$$

Process prime $3$:

  • multiply all multiples of 3 by 3.

Then:

$$\operatorname{rad}(6)=2\cdot3=6,$$

$$\operatorname{rad}(9)=3.$$

And so on.

The complexity is essentially:

$$O(n \log\log n),$$

which is extremely fast for $100000$.


Python implementation

LIMIT = 100000

# rad[n] will store rad(n)
rad = [1] * (LIMIT + 1)

# Sieve to compute radicals
for p in range(2, LIMIT + 1):
    # If rad[p] is still 1, then p is prime
    if rad[p] == 1:
        # Multiply p into every multiple of p
        for multiple in range(p, LIMIT + 1, p):
            rad[multiple] *= p

# Build list of (rad(n), n)
values = [(rad[n], n) for n in range(1, LIMIT + 1)]

# Sort by rad(n), then by n automatically
values.sort()

# E(10000) is the 10000th element (1-indexed)
answer = values[9999][1]

print(answer)

Code walkthrough

Step 1: Initialize radicals

rad = [1] * (LIMIT + 1)

We begin with every radical equal to 1 because we will multiply in distinct prime factors as we discover them.


Step 2: Detect primes

if rad[p] == 1:

Initially only primes retain value 1 when reached:

  • composite numbers have already had smaller prime factors multiplied into them,
  • primes have not.

Thus this acts as a primality test.


Step 3: Multiply prime into all multiples

for multiple in range(p, LIMIT + 1, p):
    rad[multiple] *= p

Each prime contributes exactly once to every multiple, which is precisely the definition of the radical.

Example:

  • when $p=2$, $12$ gets multiplied by 2,
  • when $p=3$, $12$ gets multiplied by 3,
  • no further multiplication occurs for repeated powers.

Hence:

$$\operatorname{rad}(12)=2\cdot3=6.$$


Step 4: Build sortable pairs

values = [(rad[n], n) for n in range(1, LIMIT + 1)]

Python tuple sorting is lexicographic, so:

values.sort()

sorts first by $\operatorname{rad}(n)$, then by $n$.

Exactly what the problem requires.


Step 5: Extract the 10000th element

Because Python uses 0-based indexing:

values[9999]

is the 10000th entry.

The second component is $n$.


Small-example verification

For $1 \le n \le 10$, the code produces:

$$(1,1), (2,2), (2,4), (2,8), (3,3), (3,9), (5,5), (6,6), (7,7), (10,10)$$

Thus:

  • $E(4)=8$,
  • $E(6)=9$,

matching the problem statement.

So the method is correct.


Final verification

The result should:

  • be between $1$ and $100000$,
  • correspond to a relatively small radical ordering,
  • and the sieve computation is exact and deterministic.

Running the algorithm gives:

$$E(10000)=21417.$$

Answer: 21417