「確率 mod 998244353」とか「期待値 mod 998244353」って何なの?という話
はじめに
分数のmodって何?AtCoderの注釈を読んでも意味わからないんだけど?といった方向けの記事です。
なおこの記事上は法(998244353や1000000007など)が素数であることを前提とします。
結論
結論から言うと、「割り算をするかわりに分母の逆元(モジュラ逆数)をかけて998244353で割った余り」です。
例
実数上の答えがだったとすると、なので、
が有理数mod上での答えとなります。
実際にであることから定義を満たしています。
実装例
Pythonの場合
モジュラ逆数はpow
関数で求めることができます。
mod = 998244353 denominator = pow(5926, -1, mod) print(3141 * denominator % mod) # -> 894983505
Python3.7以前では第2引数に負数を与えられないため、mod - 2
とすれば求まります。(後述)
mod = 998244353 denominator = pow(5926, mod - 2, mod) print(3141 * denominator % mod) # -> 894983505
C++の場合
AtCoder Library (ACL)にinv_mod
関数があるため、それを利用できます。
オーバーフローには注意です。
#include <bits/stdc++.h> using namespace std; #include <atcoder/all> using namespace atcoder; int main() { int mod = 998244353; long long denominator = inv_mod(5926, mod); cout << 3141 * denominator % mod << endl; // -> 894983505 }
また、Modint
を用いて普段の割り算のように計算することもできます。
#include <bits/stdc++.h> using namespace std; #include <atcoder/all> using namespace atcoder; using mint = modint998244353; int main() { mint ans = 3141; ans /= 5926; cout << ans.val() << endl; // -> 894983505 }
ACLが使えない場合は「c++ 逆元」などで検索すると有志のライブラリが見つかります。
PythonでもModintを使いたい場合は私のライブラリにあるので、それを利用すると良いです。
class Modint(int): mod = 998244353 # (以下略) ans = Modint(3141) ans /= 5926 print(ans) # -> 894983505
性質
例
足し算
なので
コインを4回投げてすべて表が出る確率
1回表の確率がなので
4回表の確率は
累乗は繰り返し二乗法で求められます。Pythonならpow(底, 冪指数, mod)
で、C++ならACLのpow_mod
やmodint.pow(冪指数)
が使えます。
MOD-2の話
のときフェルマーの小定理より
両辺にをかけて
のときも上の式に代入するとのとき成立することがわかる。
注意点
階乗が絡むとき
のとき、となるが基本的にとなることを間違えやすいです。
正しくは、フェルマーの小定理よりとして となります。
例:を求めよ
なので
何度も同じ数で割る場合
逆元を求める操作はなので、DPなどで何度も同じ数で割るときなどに毎回計算すると遅くなってしまいます。
このときは逆元をあらかじめ求めておいて、掛け算をすると少し速くなります。
例:ABC298-Eではやの逆元を先に求めておくと速くなります。
最後に
昔いまいち概念が理解できなかったので記事にしてみました。
というかこんな注釈で理解できる人っているんですかね……?
*1:998244353と0は互いに素ではないので、両辺に0をかけることはできない