2021年3月22日月曜日

小さい数を法とする二項係数

(nk)mod  M\binom{n}{k} \mod Mを求めたいが、MMが合成数だったりkMk \ge Mだったりして階乗の逆元が得られない場合について。nnが小さければパスカルの三角形を作ればよいが、MMが小さい場合にも方法がある。https://discuss.codechef.com/t/your-approach-to-solve-sandwich/14618/13 より。

MMをいくつかの互いに素な因数に分解してそれぞれを法として二項係数を求めれば、CRTでmod  M\mod Mの値が得られる。したがって、次の問題が解ければよい:

問題: 素数の累乗pep^eが与えられた時に(nk)mod  pe\binom{n}{k} \mod p^eを求めよ。

f(x,p):=f(x, p) := 階乗x!x!を非負整数e,qe, qを使ってx!=peqx! = p^e qと表す時の最大のqq

とする。言い換えると、f(x,p)f(x, p)x!x!ppで割れる限り割ったものである。n!=pe1f(n,p),k!=pe2f(k,p),(nk)!=pe3f(nk,p)n! = p^{e_1}f(n, p), k! = p^{e_2}f(k, p), (n-k)! = p^{e_3}f(n-k, p)とするとき、二項係数(nk)\binom{n}{k}は次のように書ける:

(nk)=pe1e2e3f(n,p)f(k,p)f(nk,p) \binom{n}{k} = \frac{p^{e_1-e_2-e_3}f(n, p)}{f(k, p)f(n-k, p)}

f(k,p),f(nk,p)f(k, p), f(n-k, p)pep^eと互いに素なのでmod  pe\mod p^eで逆元を持つ。したがって、e1,e2,e3e_1, e_2, e_3f(n,p),f(k,p),f(nk,p)f(n, p), f(k, p), f(n-k, p)が求まれば二項係数を計算できる。

前者、つまりe1,e2,e3e_1, e_2, e_3を求めるほうは簡単で、一般に階乗x!x!ppで割れる回数はi=1x/pi\sum_{i=1}^\infty \lfloor x/p^i \rfloorに等しく(ルジャンドルの公式)、この式で素朴に計算すればO(logx){O}(\log x)で求まる。

次にmod  pe\mod p^ef(x,p)f(x, p)を計算する問題を考える。f(x,p)f(x, p)x!x!から素因数ppを除いたものなので、ひとまず階乗からppの倍数だけ分離して考えてみる: XX1,2,...,x1, 2, ..., xのうちのppの倍数でない数の総積とするとき、f(x,p)=Xf(x/p,p)f(x, p) =Xf(\lfloor x/p \rfloor, p)が成り立ち、O(logx){O}(\log x)回の再帰でf(0,p)=1f(0, p)=1に帰着してf(x,p)f(x, p)が求まる。 (実際にはループでよい。)

あとはXXmod  pe\mod p^eで求める方法が問題になるが、ppの倍数の位置はmod  pe\mod p^eで周期的なことを利用できる。最初に「ppの倍数だけ飛ばした階乗mod  pe\mod p^e」の長さpep^eのテーブルを作っておけば、XXは繰り返し二乗法でO(logx){O}(\log x)で計算できる。

全体の計算量は

  • build: O(pe){O}(p^e)
  • query: O((logn)2){O}((\log n)^2)

と評価できる。また、冒頭の任意のMMを法とした二項係数を求める問題に関しては、

  • build: O(M){O}(M)
  • query: O((logn)2loglogM){O}((\log n )^2 \log \log M)

となる。(loglogM\log \log MMMの異なる素因数の個数の評価。)


Lucasの定理の素数の累乗に関する拡張を使う方法もあるらしい。たぶんこちらのほうが速い。 https://arxiv.org/pdf/1301.0252.pdf