注意:この記事は未完成です。こちらの記事を参考にお願いします
wakabame.hatenablog.com
の素数 で割った余りを求める方法について解説します. は" 個の異なるものから 個を選ぶときの組み合わせの数"を表しており, 階乗記号 を用いて,
と表されます. 階乗の大きさは指数関数的に増大していくので, 分母と分子は非常に大きな数になりえます. 素数 で割った余りさえわかればよいことを用いて, どうにか計算量を減らしていきましょう.
前回記事では法の下での掛け算について
という性質があることを紹介しました. 一方で法の下での割り算では, が整数だと期待できたとしてもこのような性質は成り立ちません. しかし, の法 における逆元 , つまり, 整数であってをみたすものを求めることができたならば,
によって求めることができます. 実は が素数であるという性質と前回の指数計算アルゴリズムから, を 効率よく求めることができます.
フェルマーの小定理
フェルマーの小定理について, 詳しくはフェルマーの小定理 - Wikipediaをご覧ください. フェルマーの小定理から特に,
つまり, が成立します. 前回の指数計算のアルゴリズムでは が大きくても高々 の計算量ですみますので, 十分に高速です.
そこで, 次のようなアルゴリズムで を求めます:
- を求める
- を求める.
- を mod の下での指数計算によって求める.
- それぞれを掛けて, で割った余りを求める.
以下がpythonのコードになります.
def comb(n,k,p): """power_funcを用いて(nCk) mod p を求める""" from math import factorial if n < 0 or k < 0 or n < k: return 0 a = factorial(n) %p b = factorial(k) %p c = factorial(n-k) %p return (a * power_func(b,p-2,p) * power_func(c,p-2,p)) % p def power_func(a,b,p): """a^b mod p を求める""" if b == 0: return 1 if b%2 == 0: d = power_func(a, b//2, p) return d*d %p if b%2 == 1: return (a * power_func(a,b-1,p )) % p
ただし は大きな素数で, 特に より大きいものを選んでください. さもなくば が の倍数になり, フェルマーの小定理を用いることが出来ません.