Skip to content

Latest commit

 

History

History
 
 

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 

README.md

modPow 関数の目的

a^b を mod で割った余りを高速で計算する
(つまり、巨大な数を直接作らず、余りだけをうまく計算する)

なぜ高速?
普通に計算すると b 回掛けるので O(b) 時間かかりますが、modPowは O(log b) 時間で終わります。


コードの流れと解説

① 初期化

let result = 1n;
a = BigInt(a) % mod;
b = BigInt(b);
  • result は最終的に答えになる変数です。最初は1からスタートします。
  • abBigInt型に変換し、さらに amod で割った余りにしておきます。
    • 例えば、( a = 12 )、( mod = 5 ) なら ( a = 2 ) になります。
    • こうすることで、無駄に大きな値を扱わなくて済みます。

② ループ開始(bが0になるまで)

while (b > 0) {
  • b(= 指数)が0になったら計算終了です。
  • その間、bをどんどん半分にしていきます。(だからlog b回しかループしない)

③ もしbが奇数なら、resultにaを掛ける

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を自乗して、bを半分にする

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)