「a^b を mod で割った余りを高速で計算する」
(つまり、巨大な数を直接作らず、余りだけをうまく計算する)
なぜ高速?
普通に計算すると b 回掛けるので O(b) 時間かかりますが、modPowは O(log b) 時間で終わります。
let result = 1n;
a = BigInt(a) % mod;
b = BigInt(b);resultは最終的に答えになる変数です。最初は1からスタートします。aとbをBigInt型に変換し、さらにaをmodで割った余りにしておきます。- 例えば、( a = 12 )、( mod = 5 ) なら ( a = 2 ) になります。
- こうすることで、無駄に大きな値を扱わなくて済みます。
while (b > 0) {- b(= 指数)が0になったら計算終了です。
- その間、bをどんどん半分にしていきます。(だからlog b回しかループしない)
if (b % 2n === 1n) {
result = (result * a) % mod;
}bが奇数なら、resultに今のaを掛けます。- そして、掛けた後はmodを取って余りだけ残します。
なぜ?
- 指数法則により、奇数の場合は [ a^b = (a^{b-1}) \times a ] だからです。
- 例えば ( 5^3 = 5^2 \times 5 ) みたいな感じです。
a = (a * a) % mod;
b = b / 2n;- aを「自分自身と掛ける」(つまり a^2 にする)
- bを2で割って半分にする(整数割り)
なぜこれをするか?
- [ a^{2x} = (a^2)^x ] という指数法則を使っているから。
- つまり、「べき乗」の計算をどんどん少ない回数で済ませられる。
return result;- 最終的な計算結果がここに入っているので、返します。
例えば a = 5, b = 23 のとき:
b = 23 (10111 in binary)
-> 最下位ビットが1 → result *= a
a = a*a, b = b/2
b = 11 (1011)
-> 最下位ビットが1 → result *= a
a = a*a, b = b/2
b = 5 (101)
-> 最下位ビットが1 → result *= a
a = a*a, b = b/2
b = 2 (10)
-> 最下位ビットが0 → スキップ
a = a*a, b = b/2
b = 1 (1)
-> 最下位ビットが1 → result *= a
a = a*a, b = b/2
b = 0 → 終了
✅ 「奇数のときだけresultを更新する」
✅ 「毎回aを自乗していく」
✅ 「bを2で割っていく」
この流れで、指数の計算を「2進数的」に高速に処理しているわけです!
| 項目 | 内容 |
|---|---|
| なにをする? | ( a^b \mod m ) を高速に計算 |
| どうやる? | aを自乗しながら、bを半分に減らす |
| ポイント | bが奇数のときだけresultに掛ける |
| 計算量 | O(log b) |