Project Euler Problem 681

Given positive integers a le b le c le d, it may be possible to form quadrilaterals with edge lengths a,b,c,d (in any or

Project Euler Problem 681

Solution

Answer: 2611227421428

For a quadrilateral with side lengths $a,b,c,d$, the maximal possible area is attained by a cyclic quadrilateral. By Brahmagupta’s formula,

$$K^2=(s-a)(s-b)(s-c)(s-d), \qquad s=\frac{a+b+c+d}{2}.$$

So the problem reduces to studying integer solutions of this expression.

Define

$$U=s-a,\quad V=s-b,\quad W=s-c,\quad T=s-d.$$

Then

$$U\ge V\ge W\ge T>0$$

because $a\le b\le c\le d$, and

$$a=s-U,\ b=s-V,\ c=s-W,\ d=s-T.$$

Also,

$$U+V+W+T=2s=a+b+c+d.$$

The maximal area becomes

$K^2=UVWT$

Hence $K$ is an integer iff $UVWT$ is a perfect square.

The side lengths are integers iff $U,V,W,T$ all have the same parity, equivalently

$$U+V+W+T \equiv 0 \pmod 2.$$

Finally, $a>0$ gives

$$s-U>0 \iff U<V+W+T.$$

So we must enumerate all ordered quadruples

$$U\ge V\ge W\ge T>0$$

such that

  1. $UVWT=k^2$ for some $1\le k\le n$,
  2. $U<V+W+T$,
  3. $U+V+W+T$ is even.

For every valid quadruple, the contribution to $SP(n)$ is

$$U+V+W+T.$$


Efficient enumeration

Fix $k\le n$. We enumerate divisors of $k^2$.

Let

$$UVWT=k^2.$$

Choose $T\mid k^2$, then $W\mid k^2/T$, and define

$$R=\frac{k^2}{TW}=UV.$$

Now $V$ must divide $R$, and

$$U=\frac{R}{V}.$$

The ordering condition $U\ge V$ implies

$$V\le \sqrt R.$$

The positivity condition becomes

$$\frac{R}{V}<V+W+T.$$

This gives a lower bound for $V$, dramatically pruning the search.

The resulting algorithm runs comfortably fast for $n=10^6$.


Python implementation

from math import isqrt
from bisect import bisect_left

def build_spf(limit):
    """Smallest prime factor sieve."""
    spf = list(range(limit + 1))

    for i in range(2, isqrt(limit) + 1):
        if spf[i] == i:
            for j in range(i * i, limit + 1, i):
                if spf[j] == j:
                    spf[j] = i

    return spf

def factorize(n, spf):
    """Prime factorization of n."""
    out = []

    while n > 1:
        p = spf[n]
        e = 0

        while n % p == 0:
            n //= p
            e += 1

        out.append((p, e))

    return out

def divisors_of_k2_upto_k(k, fac):
    """
    Generate all divisors d of k^2 with d <= k.
    """

    divs = [1]

    for p, e in fac:
        current = divs[:]

        pe = 1
        for _ in range(2 * e):
            pe *= p

            for d in current:
                v = d * pe
                if v <= k:
                    divs.append(v)

    divs.sort()
    return divs

def SP(n):
    spf = build_spf(n)

    total = 0

    for k in range(1, n + 1):

        k2 = k * k

        fac = factorize(k, spf)

        divs = divisors_of_k2_upto_k(k, fac)

        dlen = len(divs)

        for ti, T in enumerate(divs):

            k2_div_T = k2 // T

            for wi in range(ti, dlen):

                W = divs[wi]

                if W > k2_div_T:
                    break

                if k2_div_T % W:
                    continue

                R = k2_div_T // W   # R = U*V

                vmax = isqrt(R)

                if vmax < W:
                    continue

                S = W + T

                # Need:
                # R/V < V + S
                #
                # Solve:
                # V^2 + S*V - R > 0

                disc = S * S + 4 * R
                root = isqrt(disc)

                vmin = (root - S) // 2 + 1

                if vmin < W:
                    vmin = W

                if vmin > vmax:
                    continue

                lo = bisect_left(divs, vmin)

                for vi in range(lo, dlen):

                    V = divs[vi]

                    if V > vmax:
                        break

                    if R % V:
                        continue

                    U = R // V

                    # positivity condition
                    if U >= V + S:
                        continue

                    perimeter = U + V + S

                    # all U,V,W,T same parity
                    if perimeter % 2 == 0:
                        total += perimeter

    return total

# verification
assert SP(10) == 186
assert SP(100) == 23238

print(SP(1_000_000))

Code walkthrough

1. Convert the geometry problem to arithmetic

Using Brahmagupta’s formula, we replace the sides by

$$(U,V,W,T)=(s-a,s-b,s-c,s-d).$$

Then:

  • area condition becomes $UVWT=k^2$,
  • ordering becomes $U\ge V\ge W\ge T$,
  • side positivity becomes $U<V+W+T$,
  • integrality becomes even perimeter.

2. Enumerate divisors

For each $k\le n$, we factor $k$ using a smallest-prime-factor sieve.

From the factorization of $k$, we generate all divisors of $k^2$ up to $k$.


3. Construct $(U,V,W,T)$

We choose:

  • $T$,
  • $W$,
  • then $V$,

and compute

$$U=\frac{k^2}{VWT}.$$

Bounds derived from the inequality

$$U<V+W+T$$

greatly reduce the search space.


4. Verify examples

The code checks:

assert SP(10) == 186
assert SP(100) == 23238

matching the statement exactly.


Final verification

The computation gives

$$SP(1,000,000)=2611227421428.$$

The value is positive and of the expected magnitude (roughly quadratic growth compared with the $n=100$ case), and all provided sample values are reproduced exactly.

Answer: 2611227421428