Lucas Theorem

Prerequisite knowledge: factorial modulo

Introduction

This article discusses the solution of modulus of large combination numbers. Combinatorial numbers, also known as binomial coefficients, refer to the expression:

(nk)=n!k!(n−k)!.\binom{n}{k} = \dfrac{n!}{k!(n-k)!}.

When the scale is small, the combination number can be solved by recurrence formula with a time complexity of O(nk)O(nk); it can also be solved in O(n)O(n) time by calculating the factorial of the numerator and denominator under a larger prime modulus p>np>n. But when the problem scale is very large (n∼1018n\sim 10^{18}), these methods are no longer applicable.

Based on Lucas' theorem and its extension, this article discusses a method that can solve the combinatorial number when the modulus is not too large (m∼106m \sim 10^6). More precisely, this method can be used whenever the unique decomposition of the modulus m=∏pieim=\prod p_i^{e_i} has a sum of prime powers (that is, ∑piei\sum p_i^{e_i}) on the order of 10610^6, since the preprocessing of the algorithm corresponds roughly to this scale.

Lucas Theorem

First, we discuss the case where the modulus is a prime number pp. In this case, Lucas theorem states:

Lucas Theorem

For the prime number pp, we have

(nk)≡(⌊n/p⌋⌊k/p⌋)(n mod pk mod p)(modp).\binom{n}{k}\equiv \binom{\lfloor n/p\rfloor}{\lfloor k/p\rfloor}\binom{n\bmod p}{k\bmod p}\pmod p.

In particular, when n<kn<k, the binomial coefficient (nk)\dbinom{n}{k} is specified as 00.

Proof using generating functions

Consider the value of (pn) mod p\displaystyle\binom{p}{n} \bmod p. because

(pn)=p!n!(p−n)!,\binom{p}{n} = \frac{p!}{n!(p-n)!},

Therefore, when n≠0,pn\neq 0,p, there is no factor pp in the denominator, but there is a factor pp in the numerator, so the fraction must be a multiple of pp, and the remainder modulo pp is 00; when n=0,pn=0,p When, the fraction is 11. therefore,

(pn)≡[n=0∨n=p](modp).\binom{p}{n} \equiv [n=0\lor n=p] \pmod p.

notes f(x)=axn+bxmf(x) = ax^n + bx^m. Generally, binomial expansion and Fermat's little theorem have

(f(x))p=(axn+bxm)p=∑k=0p(pk)(axn)k(bxm)p−k≡apxpn+bpxpm≡a(xp)n+b(xp)m=f(xp)(modp).\begin{aligned} (f(x))^p &= \left(ax^n + bx^m\right)^p \\ &= \sum_{k=0}^p\binom{p}{k}(ax^n)^k(bx^m)^{p-k}\\ &\equiv a^px^{pn} + b^px^{pm} \\ &\equiv a(x^p)^n+b(x^p)^m\\ &= f(x^p) \pmod p. \end{aligned}

In particular, the congruence of the third line makes use of the conclusion explained above, that is, only when k=0,pk=0,p, the number of combinations is not a multiple of pp.

Using this conclusion to examine the binomial expansion:

(1+x)n=(1+x)p⌊n/p⌋(1+x)n mod p≡(1+xp)⌊n/p⌋(1+x)n mod p(modp).\begin{aligned} (1+x)^n &= (1+x)^{p\lfloor n/p\rfloor}(1+x)^{n\bmod p} \\ &\equiv (1+x^p)^{\lfloor n/p\rfloor}(1+x)^{n\bmod p} \pmod p. \end{aligned}

On the left-hand side of the equation, the coefficient of the term xkx^k is

(nk) mod p.\binom{n}{k}\bmod p.

We now calculate the coefficient of the middle term xkx^k on the right side of the equation. The degree of each term in the first factor must be a multiple of pp, and the degree of each term in the second factor must be smaller than pp. The only way to decompose kk into the sum of these two parts is by division with remainder: k=p⌊k/p⌋+(k mod p)k=p\lfloor k/p\rfloor +(k\bmod p). Therefore, the first factor can only contribute its p⌊k/p⌋p\lfloor k/p\rfloor order terms, and the second factor can only contribute its k mod pk\bmod p order terms. Therefore, the xkx^k coefficient in the equation on the right is the product of the coefficients of the terms contributed by the two factors:

(⌊n/p⌋⌊k/p⌋)(n mod pk mod p) mod p.\binom{\lfloor n/p\rfloor}{\lfloor k/p\rfloor}\binom{n\bmod p}{k\bmod p}\bmod p.

Equating the coefficients on both sides are equal, we get Lucas' theorem.

Conclusion proof using factorial modulo

A proof method based on the relevant conclusions of factorial modulo is provided here to facilitate the connection with the method in the exLucas section later. Known binomial coefficients

(nk)=n!k!(n−k)!.\binom{n}{k} = \dfrac{n!}{k!(n-k)!}.

Separating the power of pp in the factorial n!n! from other factors to get the decomposition:

n!=pνp(n!)(n!)p.n! = p^{\nu_p(n!)}(n!)_p.

This gives the expression of the binomial coefficient:

(nk)=pνp(n!)−νp(k!)−νp((n−k)!)(n!)p(k!)p((n−k)!)p.\binom{n}{k} = p^{\nu_p(n!)-\nu_p(k!)-\nu_p((n-k)!)}\dfrac{(n!)_p}{(k!)_p((n-k)!)_p}.

The power νp(n!)\nu_p(n!) and factorial remainder (n!)p mod p(n!)_p\bmod p:

νp(n!)=⌊n/p⌋+νp(⌊n/p⌋!),(n!)p≡(−1)⌊n/p⌋⋅(n mod p)!⋅(⌊n/p⌋!)p(modp).\begin{aligned} \nu_p(n!) &= \lfloor n/p\rfloor+\nu_p( \lfloor n/p\rfloor!),\\ (n!)_p &\equiv (-1)^{\lfloor n/p\rfloor}\cdot (n\bmod p)!\cdot (\lfloor n/p\rfloor!)_p\pmod p. \end{aligned}

The former is the corollary of Legendre's formula, and the latter is the corollary of Wilson's theorem.

Substituting the recurrence into the expression of the binomial coefficient and sorting it out, we get:

(nk)≡(−p)⌊n/p⌋−⌊k/p⌋−⌊(n−k)/p⌋⋅(n mod p)!(k mod p)!((n−k) mod p)!⋅pνp(⌊n/p⌋!)−νp(⌊k/p⌋!)−νp(⌊(n−k)/p⌋!)(⌊n/p⌋!)p(⌊k/p⌋!)p(⌊(n−k)/p⌋!)p(modp).\begin{aligned} \binom{n}{k} &\equiv (-p)^{\lfloor n/p\rfloor-\lfloor k/p\rfloor-\lfloor(n-k)/p\rfloor}\cdot\dfrac{(n\bmod p)!}{(k\bmod p)!((n-k)\bmod p)!} \\ &\quad \cdot p^{\nu_p(\lfloor n/p\rfloor!)-\nu_p(\lfloor k/p\rfloor!)-\nu_p(\lfloor(n-k)/p\rfloor!)}\dfrac{(\lfloor n/p\rfloor!)_p}{(\lfloor k/p\rfloor!)_p(\lfloor(n-k)/p\rfloor!)_p} \pmod p. \end{aligned}

Now examine the value of ⌊n/p⌋−⌊k/p⌋−⌊(n−k)/p⌋\lfloor n/p\rfloor-\lfloor k/p\rfloor-\lfloor(n-k)/p\rfloor. because there is

n=⌊n/p⌋p+(n mod p),k=⌊k/p⌋p+(k mod p),n−k=⌊(n−k)/p⌋p+((n−k) mod p),\begin{aligned} n &= \lfloor n/p\rfloor p + (n\bmod p),\\ k &= \lfloor k/p\rfloor p + (k\bmod p),\\ n-k &= \lfloor (n-k)/p\rfloor p + ((n-k)\bmod p),\\ \end{aligned}

So, by subtracting the last two equations from the first equation, we get

(⌊n/p⌋−⌊k/p⌋−⌊(n−k)/p⌋)p=(k mod p)+((n−k) mod p)−(n mod p).(\lfloor n/p\rfloor-\lfloor k/p\rfloor-\lfloor(n-k)/p\rfloor)p = (k\bmod p)+((n-k)\bmod p)-(n\bmod p).

On the right-hand side of the equation, the sum of the first two terms is strictly less than 2p2p, and the third term n mod pn\bmod p is the remainder of the sum of the first two terms, so the right side must be non-negative, but smaller than 2p2p, and it needs to be a multiple of pp, so it can only be 00 or pp. This means that ⌊n/p⌋−⌊k/p⌋−⌊(n−k)/p⌋\lfloor n/p\rfloor-\lfloor k/p\rfloor-\lfloor(n-k)/p\rfloor can only be 00 or 11:

  • If it is 00, then (n mod p)=(k mod p)+((n−k) mod p)(n\bmod p) = (k\bmod p)+((n-k)\bmod p) is also true. Therefore, the exponent of the first factor in the above formula is 00, which is equal to one; the second factor is (n mod pk mod p)\dbinom{n\bmod p}{k\bmod p}; and the third factor, as can be seen from the previous expansion, is equal to (⌊n/p⌋⌊k/p⌋)\dbinom{\lfloor n/p\rfloor}{\lfloor k/p\rfloor}. At this point, Lucas’ formula holds;

  • If it is 11, then the first factor has exponent 11 and that factor is equal to zero, so the remainder of the binomial coefficient is zero. At the same time, (n mod pk mod p)\dbinom{n\bmod p}{k\bmod p} on the right side of the equation to be proved by Lucas' theorem must also be zero, because there must be (n mod p)<(k mod p)(n\bmod p)<(k\bmod p) at this time; otherwise, there will be

    ((n−k) mod p)=p+(n mod p)−(k mod p)≥p.((n-k)\bmod p) = p + (n\bmod p) - (k\bmod p) \ge p.

    This obviously contradicts the definition of remainder.

By combining the two situations, the required Lucas theorem is obtained. This proof shows that when solving combinatorial numbers modulo prime numbers, the results obtained by using the Lucas theorem and the exLucas algorithm are equivalent.

The Lucas theorem points out that when the modulus is a prime number pp, the calculation of large combinatorial numbers can be converted into the calculation of smaller combinatorial numbers. In the right formula, the first combination number can continue to recurse until $n,k [!example]+ means

long long Lucas(long long n, long long k, long long p) {
  if (k == 0) return 1;
  return (C(n % p, k % p, p) * Lucas(n / p, k / p, p)) % p;
}

Among them, C(n, k, p) is used to calculate the number of small-scale combinations.

The recursion is performed at most O(log⁡pn)O(\log_p n) times, so the complexity of the algorithm is O(f(p)+g(p)log⁡pn)O(f(p)+g(p)\log_p n), where f(p)f(p) is the complexity of the preprocessing combination number, and g(p)g(p) is the complexity of the single calculation combination number.

Reference Implementation

After preprocessing the factorials and their inverses up to pp in O(p)O(p) time, the following reference implementation computes each binomial coefficient in O(1)O(1) time:

reference implementation
#include <iostream>
#include <vector>

class BinomModPrime {
  int p;
  std::vector<int> fa, ifa;

  // Calculate binom(n, k) mod p for n, k < p.
  int calc(int n, int k) {
    if (n < k) return 0;
    long long res = fa[n];
    res = (res * ifa[k]) % p;
    res = (res * ifa[n - k]) % p;
    return res;
  }

 public:
  BinomModPrime(int p) : p(p), fa(p), ifa(p) {
    // Factorials mod p till p.
    fa[0] = 1;
    for (int i = 1; i < p; ++i) {
      fa[i] = (long long)fa[i - 1] * i % p;
    }
    // Inverse of factorials mod p till p.
    ifa[p - 1] = p - 1;  // Wilson's theorem.
    for (int i = p - 1; i; --i) {
      ifa[i - 1] = (long long)ifa[i] * i % p;
    }
  }

  // Calculate binom(n, k) mod p.
  int binomial(long long n, long long k) {
    long long res = 1;
    while (n || k) {
      res = (res * calc(n % p, k % p)) % p;
      n /= p;
      k /= p;
    }
    return res;
  }
};

int main() {
  int t, p;
  std::cin >> t >> p;
  BinomModPrime bm(p);
  for (; t; --t) {
    long long n, k;
    std::cin >> n >> k;
    std::cout << bm.binomial(n, k) << '\n';
  }
  return 0;
}

The time complexity of this implementation is O(p+Tlog⁡pn)O(p+T\log_p n), where TT is the number of queries.

exLucas algorithm

Lucas' theorem requires that the modulus pp must be a prime number. If pp is not a prime number, the exLucas algorithm needs to be used. Despite its name, this algorithm does not use Lucas' theorem in actual operation. Its key step is factorials modulo prime powers. The second proof above points out its connection with Lucas' theorem.

Prime-Power Modulus Case

First consider the case where the modulus is a prime power pαp^\alpha. By separating the powers of pp from other powers in the factorial n!n!, we can get the decomposition:

n!=pνp(n!)(n!)p.n! = p^{\nu_p(n!)}(n!)_p.

Among them, νp(n!)\nu_p(n!) is the power of pp in the prime factorization of n!n!, and (n!)p(n!)_p is obviously coprime with pp. Therefore, the combinatorial number can be written as:

(nk)=pνp(n!)−νp(k!)−νp((n−k)!)(n!)p(k!)p((n−k)!)p.\binom{n}{k} = p^{\nu_p(n!)-\nu_p(k!)-\nu_p((n-k)!)}\dfrac{(n!)_p}{(k!)_p((n-k)!)_p}.

νp(n!)\nu_p(n!) and so on in the formula can be calculated by Legendre formula, and (n!)p(n!)_p and so on can be calculated by recurrence relation. Since the latter is relatively prime with pαp^\alpha, the inverse of the product in the denominator can be calculated by extended Euclidean algorithm. The problem will be solved.

Note that if the power is νp(n!)−νp(k!)−νp((n−k)!)≥α\nu_p(n!)-\nu_p(k!)-\nu_p((n-k)!)\ge\alpha, the remainder must be zero and there is no need to do any more calculations.

General Modulus Case

For the case where mm is a general composite number, just do prime factorization on it first:

m=p1α1p2α2⋯psαs.m = p_1^{\alpha_1}p_2^{\alpha_2}\cdots p_s^{\alpha_s}.

Then, calculate the remainders of the combinations (nk)\dbinom{n}{k} modulo piαip_i^{\alpha_i}, and get ss congruence equations:

{(nk)≡r1,(modp1α1),(nk)≡r2,(modp2α2),⋯(nk)≡rs,(modpsαs).\begin{cases} \dbinom{n}{k} \equiv r_1, &\pmod{p_1^{\alpha_1}}, \\ \dbinom{n}{k} \equiv r_2, &\pmod{p_2^{\alpha_2}}, \\ \quad\quad\cdots\\ \dbinom{n}{k} \equiv r_s, &\pmod{p_s^{\alpha_s}}. \end{cases}

Finally, use Chinese remainder theorem to find the remainder modulo mm.

Reference Implementation

Finally, the reference implementation of the template title binomial coefficient is given.

reference implementation
#include <iostream>
#include <vector>

// Extended Euclid.
void ex_gcd(int a, int b, int& x, int& y) {
  if (!b) {
    x = 1;
    y = 0;
  } else {
    ex_gcd(b, a % b, y, x);
    y -= a / b * x;
  }
}

// Inverse of a mod m.
int inverse(int a, int m) {
  int x, y;
  ex_gcd(a, m, x, y);
  return (x % m + m) % m;
}

// Coefficient in CRT.
int crt_coeff(int m_i, int m) {
  long long mm = m / m_i;
  mm *= inverse(mm, m_i);
  return mm % m;
}

// Binominal Coefficient Calculator Modulo Prime Power.
class BinomModPrimePower {
  int p, a, pa;
  std::vector<int> f;

  // Obtain multiplicity of p in n!.
  long long nu(long long n) {
    long long count = 0;
    do {
      n /= p;
      count += n;
    } while (n);
    return count;
  }

  // Calculate (n!)_p mod pa.
  long long fact_mod(long long n) {
    bool neg = p != 2 || pa <= 4;
    long long res = 1;
    while (n > 1) {
      if ((n / pa) & neg) res = pa - res;
      res = res * f[n % pa] % pa;
      n /= p;
    }
    return res;
  }

 public:
  BinomModPrimePower(int p, int a, int pa) : p(p), a(a), pa(pa), f(pa) {
    // Pretreatment.
    f[0] = 1;
    for (int i = 1; i < pa; ++i) {
      f[i] = i % p ? (long long)f[i - 1] * i % pa : f[i - 1];
    }
  }

  // Calculate Binom(n, k) mod pa.
  int binomial(long long n, long long k) {
    long long v = nu(n) - nu(n - k) - nu(k);
    if (v >= a) return 0;
    auto res = fact_mod(n - k) * fact_mod(k) % pa;
    res = fact_mod(n) * inverse(res, pa) % pa;
    for (; v; --v) res *= p;
    return res % pa;
  }
};

// Binominal Coefficient Calculator.
class BinomMod {
  int m;
  std::vector<BinomModPrimePower> bp;
  std::vector<long long> crt_m;

 public:
  BinomMod(int n) : m(n) {
    // Factorize.
    for (int p = 2; p * p <= n; ++p) {
      if (n % p == 0) {
        int a = 0, pa = 1;
        for (; n % p == 0; n /= p, ++a, pa *= p);
        bp.emplace_back(p, a, pa);
        crt_m.emplace_back(crt_coeff(pa, m));
      }
    }
    if (n > 1) {
      bp.emplace_back(n, 1, n);
      crt_m.emplace_back(crt_coeff(n, m));
    }
  }

  // Calculate Binom(n, k) mod m.
  int binomial(long long n, long long k) {
    long long res = 0;
    for (size_t i = 0; i != bp.size(); ++i) {
      res = (bp[i].binomial(n, k) * crt_m[i] + res) % m;
    }
    return res;
  }
};

int main() {
  int t, m;
  std::cin >> t >> m;
  BinomMod bm(m);
  for (; t; --t) {
    long long n, k;
    std::cin >> n >> k;
    std::cout << bm.binomial(n, k) << '\n';
  }
  return 0;
}

This algorithm decomposes the modulus mm into prime powers during preprocessing, and then preprocesses all pαp^\alpha by preprocessing the product of all natural numbers from 11 to pαp^\alpha that are not multiples of pp, as well as its corresponding coefficients when merging answers to the Chinese Remainder Theorem. The time complexity of preprocessing is O(m+∑ipiαi)O(\sqrt{m}+\sum_ip_i^{\alpha_i}). Each time you ask, the complexity is O(log⁡m+∑ilog⁡pin)O(\log m+\sum_i\log_{p_i}n). The two items in the complexity are the complexity of calculating the inverse element and calculating the power and factorial remainder.

Exercises

This content comes from OI Wiki: Lucas Theorem. See the original for authors and revision history. AIOJ has adapted the formatting, in-site links, and resource paths.

Content is shared under CC BY-SA 4.0 and SATA license; additional terms are governed by the original notice. Thanks to the OI Wiki authors and contributors; per the SATA terms, please visit the OI Wiki repository and star it to show your support.