べき乗計算のためのアルゴリズムを素直に書けば、以下のようになる。
w=1 for i=1 to n w=w*p next i print w
もちろん、これでまちがっていない。
しかし、このアルゴリズムの処理時間はnの値に比例する。
従って、nが極端に大きい場合、例えば、n=10100のような場合には、完全にお手上げである。
このループの回数を減らす方法を考える。
例えば、2100の計算を考えてみる。
上のやり方なら、掛算を100回やらなければならない。
しかし、
2100=(250)2
であるから、250を上の方法で計算してやると、掛算回数は50回。
よって、2100は51回の掛算回数で求められる。
これは、最初の100回に比べて、ほぼ半分の回数である。
ならば、250自体についても同じようにやってみる。
250=(225)2
だから、掛算回数は、27回となる。
225=(212)2×2
とすると、掛算回数は、12+1+1+1+1=16回。これをずっと押し進めると、
2100=(((((22×2)2)2)2×2)2)2
となり、掛算回数は、8回となる。
これをアルゴリズムとして記述すると、
1. w=1とする(解を格納するための変数) 2. べきnを2進展開する 3. 上位ビットから順に見ていく ・1ならw=w2×p ・0ならw=w2 4. 最下位ビットまで計算したときのwの値が求める解、w=pnとなる
2進展開を格納するために配列を取っておく必要があるが、その大きさは、
n = 2x log n = log 2x = x log 2 ∴ x = int((log n)/(log 2))
となる。
また掛算回数は、最大2x=2int((log n)/(log 2))である。
この値は、nの桁数の約2倍と思ってよい。
このアルゴリズムにより、べき数がひじょうに大きい場合のべき乗を計算することができる。
プログラムは、以下のとおり。
10 ' power 20 print "p^n" 30 input "p=";P:W=P:I%=0 40 input "n=";N:dim A%(int(log(N)/log(2))) 50 while N>1 60 N=N\2:inc I%:if res=0 then A%(I%)=0 else A%(I%)=1 70 wend 80 for J%=I% to 1 step -1 90 if A%(J%)=0 then W=W^2 else W=P*W^2 100 next J% 110 print W
UBASICでは、桁数の許す限り、p^n で計算が可能である。
pnのかわりに、pnをmで割った余りが知りたいという場合も多い。
基本的には、上記プログラムで最後の掛算のところで、mで割った余りを取ればよい。
プログラムは、以下のとおり。
10 ' modpow 20 print "p^n@m" 30 input "p=";P:W=P:I%=0 40 input "n=";N:dim A%(int(log(N)/log(2))):input M 50 while N>1 60 N=N\2:inc I%:if res=0 then A%(I%)=0 else A%(I%)=1 70 wend 80 for J%=I% to 1 step -1 90 if A%(J%)=0 then W=W^2@M else W=P*W^2@M 100 next J% 110 print W
UBASICでは、modpow(p,n,m) という組み込み関数になっている。
この、2進展開して各ビットの値により処理を決める、
というテクニックは、後の Lucas 列の計算でも使用する。
前 | この章の目次 | 次 |
---|