Project Euler Problem 881

For a positive integer n create a graph using its divisors as vertices.

Project Euler Problem 881

Solution

Answer: 205702861096933200

Let

$$n=\prod_{i=1}^r p_i^{a_i}$$

with distinct primes $p_i$ and exponents $a_i\ge 1$.

We are asked to study the graph on the divisors of $n$:

  • vertices = divisors of $n$,
  • edge $a\to b$ if $a<b$ and $b/a$ is prime.

The graph is layered by distance from $n$, and $g(n)$ is the largest layer size.

We want the smallest $n$ such that

$$g(n)\ge 10^4.$$


1. Mathematical analysis

Step 1 — Encode divisors by exponent vectors

Every divisor of $n$ has the form

$$d=\prod p_i^{e_i}, \qquad 0\le e_i\le a_i.$$

An edge corresponds to multiplying/dividing by exactly one prime, i.e. changing exactly one exponent by $1$.

Starting from $n$, whose exponent vector is

$$(a_1,a_2,\dots,a_r),$$

moving one edge downward decreases one coordinate by $1$.

Therefore the distance from $n$ to divisor $d$ is

$$\sum_{i=1}^r (a_i-e_i).$$

So the vertices at level $k$ are exactly those divisors satisfying

$$\sum (a_i-e_i)=k.$$

Equivalently, define

$$b_i=a_i-e_i,\qquad 0\le b_i\le a_i.$$

Then the number of vertices at level $k$ equals the number of solutions of

$$b_1+b_2+\cdots+b_r=k, \qquad 0\le b_i\le a_i.$$

That is precisely the coefficient of $x^k$ in

$$\prod_{i=1}^r (1+x+\cdots+x^{a_i}).$$

Hence:

$$g(n)=\max_k [x^k]\prod_{i=1}^r (1+x+\cdots+x^{a_i}).$$


Step 2 — The optimization problem

The primes themselves do not affect $g(n)$; only the exponent multiset matters.

To minimize $n$, once the exponents are fixed, we should assign the largest exponents to the smallest primes:

$$a_1\ge a_2\ge \cdots$$

paired with

$$2,3,5,7,\dots$$

because of the rearrangement inequality.

Thus the problem becomes:

Find nonincreasing exponents $a_1\ge a_2\ge\cdots\ge0$ such that

$$\max_k [x^k]\prod (1+x+\cdots+x^{a_i})\ge 10^4,$$

while minimizing

$$n=\prod p_i^{a_i}.$$


For a candidate exponent list $(a_1,\dots,a_r)$:

  • compute the polynomial coefficients by dynamic programming,
  • take the maximum coefficient,
  • recursively search exponent patterns in nonincreasing order,
  • prune whenever the current number already exceeds the best found.

This search is very small because:

  • coefficients grow quickly,
  • the optimal exponents are tiny.

The optimal exponent pattern turns out to be

$$(4,3,2,2,2,1,1,1,1,1,1,1).$$

Thus

$$n = 2^4\cdot3^3\cdot5^2\cdot7^2\cdot11^2 \cdot13\cdot17\cdot19\cdot23\cdot29\cdot31\cdot37.$$

The corresponding maximal coefficient is

$$10130>10000.$$


2. Python implementation

from functools import lru_cache

TARGET = 10_000

# Enough small primes for the optimal solution
PRIMES = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43]

best_n = float('inf')
best_exp = None

@lru_cache(None)
def max_level_size(exponents):
    """
    Return the maximum coefficient of

        ∏ (1 + x + ... + x^a)

    where exponents = (a1, a2, ...).
    """

    poly = [1]

    for a in exponents:
        new_poly = [0] * (len(poly) + a)

        for i, c in enumerate(poly):
            for j in range(a + 1):
                new_poly[i + j] += c

        poly = new_poly

    return max(poly)

def dfs(index, last_exp, current_n, exponents):
    """
    Depth-first search over nonincreasing exponent sequences.
    """

    global best_n, best_exp

    # Current g(n)
    g = max_level_size(tuple(exponents))

    # Success
    if g >= TARGET:
        if current_n < best_n:
            best_n = current_n
            best_exp = exponents.copy()
        return

    # No more primes available
    if index >= len(PRIMES):
        return

    # Prune
    if current_n >= best_n:
        return

    p = PRIMES[index]
    power = 1

    # Exponents must be nonincreasing
    for e in range(1, last_exp + 1):
        power *= p
        new_n = current_n * power

        if new_n >= best_n:
            break

        exponents.append(e)

        dfs(index + 1, e, new_n, exponents)

        exponents.pop()

dfs(0, 20, 1, [])

print("Best n =", best_n)
print("Exponents =", best_exp)
print("g(n) =", max_level_size(tuple(best_exp)))

3. Code walkthrough

max_level_size

We build the generating polynomial

$$\prod (1+x+\cdots+x^{a_i})$$

coefficient-by-coefficient.

Example:

For exponents $(2,1)$,

$$(1+x+x^2)(1+x) = 1+2x+2x^2+x^3.$$

Hence $g(n)=2$.


We recursively build exponent lists:

  • exponents are forced to be nonincreasing,
  • exponent $e_i$ is attached to the $i$-th prime,
  • we stop if the current $n$ already exceeds the best known answer.

Verification on the example $n=45$

$$45 = 3^2 \cdot 5^1.$$

So the polynomial is

$$(1+x+x^2)(1+x) = 1+2x+2x^2+x^3.$$

Largest coefficient:

$$g(45)=2,$$

matching the statement.


Verification on $n=5040$

$$5040=2^4\cdot3^2\cdot5\cdot7.$$

Polynomial:

$$(1+x+x^2+x^3+x^4) (1+x+x^2) (1+x)^2.$$

Its largest coefficient is $12$, matching the problem statement.


4. Final verification

The optimal exponent pattern found is

$$(4,3,2,2,2,1,1,1,1,1,1,1),$$

giving

$$g(n)=10130.$$

Any smaller exponent configuration either:

  • produces $g(n)<10000$, or
  • yields a larger number after assigning exponents optimally to primes.

Thus the minimum is

$$n= 2^4\cdot3^3\cdot5^2\cdot7^2\cdot11^2 \cdot13\cdot17\cdot19\cdot23\cdot29\cdot31\cdot37.$$

Multiplying out:

$$n=205702861096933200.$$

Answer: 205702861096933200