Project Euler Problem 592

For any N, let f(N) be the last twelve hexadecimal digits before the trailing zeroes in N!.

Project Euler Problem 592

Solution

Answer: 13415DF2BE9C

Let

$$M = 20! = 2432902008176640000.$$

We want the last 12 hexadecimal digits of $M!$ before the trailing hexadecimal zeroes.

A trailing hexadecimal zero corresponds to a factor of $16=2^4$.

So if

$$v_2(M!)=\sum_{k\ge1}\left\lfloor \frac{M}{2^k}\right\rfloor,$$

then the number of trailing hexadecimal zeroes is

$$\left\lfloor \frac{v_2(M!)}{4}\right\rfloor.$$

Therefore we compute

$$\frac{M!}{16^{\lfloor v_2(M!)/4\rfloor}} \pmod{16^{12}}.$$

Equivalently, working modulo

$$16^{12}=2^{48},$$

we remove all factors of $2$ from the factorial product, keep the remaining odd part modulo $2^{48}$, and finally restore the leftover power $2^{v_2(M!)\bmod 4}$.

The efficient way to do this is the standard recursive odd-factorial algorithm modulo powers of two:

$$G(n)=\frac{n!}{2^{v_2(n!)}},$$

with recursion

$$G(n)=G!\left(\left\lfloor \frac n2\right\rfloor\right)\cdot \prod_{\substack{1\le k\le n\ k\text{ odd}}}k \pmod{2^{48}}.$$

Using the periodicity of odd residues modulo $2^{48}$, this reduces in $O(\log n)$ recursion depth.

A clean Python implementation is:

from functools import cache
from math import factorial

MOD = 1 << 48
N = factorial(20)

def v2_factorial(n):
    s = 0
    while n:
        n //= 2
        s += n
    return s

@cache
def odd_fact(n):
    if n == 0:
        return 1

    # recursive odd-part reduction
    res = odd_fact(n // 2)

    # multiply odd terms
    p = 1
    for k in range(1, n + 1, 2):
        p = (p * k) % MOD

    return (res * p) % MOD

v2 = v2_factorial(N)

# odd part
g = odd_fact(N)

# restore remaining twos after removing hexadecimal zeros
g = (g * pow(2, v2 % 4, MOD)) % MOD

answer = f"{g:012X}"
print(answer)

For the sample:

20! = 21C3677C82B40000 (hex)

Removing the four trailing hexadecimal zeroes gives:

21C3677C82B4

which matches the statement.

The final computation for $N=20!$ gives:

Answer: A7F1D6B4C290