Purpose: Compute binomial coefficients C(n, k) mod p for a prime p, even when n and k are far too large to compute C(n,k) directly, in O(log_p n) time.
Algorithm
- Write
nandkin basep:n = n_m n_(m-1) ... n_1 n_0andk = k_m k_(m-1) ... k_1 k_0(digits in basep, padding with leading zeros as needed). - Lucas’ theorem states:
C(n, k) mod p = Π C(n_i, k_i) mod pover all corresponding base-pdigit pairs. - If any
k_i > n_ifor some digit position, that digit’sC(n_i, k_i) = 0, making the entire product 0 — meaningC(n, k) ≡ 0 (mod p). - Compute each small
C(n_i, k_i) mod pdirectly (since digits are in[0, p-1], this is a small, direct computation via a precomputed factorial table modp). - Multiply all digit-wise results together modulo
pto get the final answer.
Code
long long powmod(long long a, long long e, long long m) {
long long r = 1; a %= m;
while (e) { if (e & 1) r = r * a % m; a = a * a % m; e >>= 1; }
return r;
}
long long smallComb(long long n, long long k, long long p, vector<long long>& fact) {
if (k < 0 || k > n) return 0;
return fact[n] * powmod(fact[k], p - 2, p) % p * powmod(fact[n - k], p - 2, p) % p;
}
long long lucasTheorem(long long n, long long k, long long p) {
vector<long long> fact(p);
fact[0] = 1;
for (int i = 1; i < p; i++) fact[i] = fact[i - 1] * i % p;
long long result = 1;
while (n > 0 || k > 0) {
long long ni = n % p, ki = k % p;
result = result * smallComb(ni, ki, p, fact) % p;
n /= p; k /= p;
}
return result;
}Paradigm
Transform and Conquer. The core idea is instance simplification: transforming an intractably large binomial coefficient computation into a product of small, directly computable binomial coefficients on individual base-p digits.
Complexity
- Time: O(p) preprocessing for factorials, O(log_p n) per query
- Space: O(p)
Proof of Correctness
Core identity (Lucas’ theorem): For a prime p and non-negative integers n = Σ n_i p^i, k = Σ k_i p^i (base-p digit expansions), C(n, k) ≡ Π C(n_i, k_i) (mod p).
Proof sketch via generating functions: Consider (1 + x)^n mod p as a polynomial over Z/pZ. By the Freshman’s Dream (a consequence of the Binomial Theorem combined with Fermat’s Little Theorem in characteristic p), (1 + x)^p ≡ 1 + x^p (mod p). Applying this repeatedly:
(1 + x)^n = (1 + x)^(Σ n_i p^i) = Π (1 + x)^(n_i p^i) = Π ((1+x)^(p^i))^(n_i) ≡ Π (1 + x^(p^i))^(n_i) (mod p)
Expanding the right-hand side, the coefficient of x^k (where k = Σ k_i p^i) is obtained by choosing, for each digit position i, exactly k_i copies of x^(p^i) out of the n_i available “slots” (1 + x^(p^i))^(n_i) — contributing a factor of C(n_i, k_i) per digit. Multiplying across all digit positions gives exactly Π C(n_i, k_i), which by matching coefficients with the left-hand side (1+x)^n’s coefficient of x^k (namely C(n, k)) proves the identity modulo p.
Why digit-wise coefficients can be computed directly: Since each digit n_i, k_i ∈ [0, p-1], C(n_i, k_i) is a small binomial coefficient computable directly via precomputed factorials modulo p (using modular inverses, since p is prime and all values less than p are invertible modulo p). ∎
Variants / Use Cases
- Kummer’s theorem → related result determining the exact power of
pdividingC(n,k), useful for composite or prime-power moduli - Binomial coefficients modulo prime powers → generalizations (e.g., Andrew Granville’s work) extend Lucas’ theorem beyond a single prime
- Combinatorics competition problems → common technique for computing
C(n,k) mod pwhenn, kare astronomically large - Counting lattice paths modulo a prime → binomial coefficients modulo p arise naturally in combinatorial path-counting problems
- Modular Pascal’s Triangle structure analysis → Lucas’ theorem explains Sierpinski-triangle-like patterns seen in Pascal’s triangle mod small primes