Combinatorics contents
Power of a Divisor in a Factorial
Find the largest x such that k^x divides n!, using Legendre's formula for primes and the prime factorization of k for composites.
Read first: Integer Factorization
Given and , find the largest such that divides . This is the question behind "how many trailing zeros does have?" () and many divisibility problems around binomial coefficients.
Prime : Legendre's formula
Among exactly numbers are multiples of , each contributing at least one factor ; of them contribute a second one, and so on. So the exponent of the prime in is
which needs only terms. Equivalently where is the sum of the base- digits of .
from math import factorial
def legendre(n, p):
"""Exponent of the prime p in n!."""
total = 0
while n:
n //= p
total += n
return total
def digit_sum(n, base):
s = 0
while n:
n, r = divmod(n, base)
s += r
return s
assert legendre(100, 5) == 24 # 100! has 24 trailing zeros (from the 5s)
assert legendre(10, 2) == 8 and legendre(10, 3) == 4
assert all(legendre(n, p) == (n - digit_sum(n, p)) // (p - 1) for n in range(0, 500) for p in (2, 3, 5, 7))
assert legendre(10 ** 18, 2) > 0 # instant for huge nComposite
Factor . For to divide we need for every prime factor, i.e. . The answer is the smallest of these bounds:
def prime_factorization(k):
factors, d = {}, 2
while d * d <= k:
while k % d == 0:
factors[d] = factors.get(d, 0) + 1
k //= d
d += 1
if k > 1:
factors[k] = factors.get(k, 0) + 1
return factors
def factorial_divisor_power(n, k):
"""Largest x with k^x | n! (k >= 2)."""
return min(legendre(n, p) // e for p, e in prime_factorization(k).items())
assert factorial_divisor_power(100, 10) == 24 # trailing zeros of 100!
assert factorial_divisor_power(10, 12) == 4 # 12 = 2^2 * 3: min(8 // 2, 4 // 1)
assert factorial_divisor_power(5, 7) == 0
def brute(n, k):
f, x = factorial(n), 0
while f % k == 0:
f //= k
x += 1
return x
for n in range(0, 120, 3):
for k in range(2, 60):
assert factorial_divisor_power(n, k) == brute(n, k), (n, k)The complexity is that of factoring (trial division up to ), plus per prime factor.
Applications
- Trailing zeros of in base :
factorial_divisor_power(n, b). - Is a binomial coefficient divisible by ? ; by Kummer's theorem it equals the number of carries when adding and in base .
- Largest power of dividing a product of many numbers: sum the exponents of the factors.
from math import comb
def binomial_valuation(n, k, p):
return legendre(n, p) - legendre(k, p) - legendre(n - k, p)
def carries(a, b, base):
count, carry = 0, 0
while a or b or carry:
s = a % base + b % base + carry
carry = 1 if s >= base else 0
count += carry
a //= base
b //= base
return count
for n in range(0, 60):
for k in range(0, n + 1):
for p in (2, 3, 5):
v, c = 0, comb(n, k)
while c % p == 0:
c //= p
v += 1
assert binomial_valuation(n, k, p) == v == carries(k, n - k, p)