2.べき乗

の計算

べき乗計算のためのアルゴリズムを素直に書けば、以下のようになる。

w=1
for i=1 to n
  w=w*p
next i
print w

もちろん、これでまちがっていない。
しかし、このアルゴリズムの処理時間はnの値に比例する。
従って、nが極端に大きい場合、例えば、n=10100のような場合には、完全にお手上げである。
このループの回数を減らす方法を考える。

例えば、2100の計算を考えてみる。 上のやり方なら、掛算を100回やらなければならない。 しかし、

100=(250)2

であるから、250を上の方法で計算してやると、掛算回数は50回。
よって、2100は51回の掛算回数で求められる。
これは、最初の100回に比べて、ほぼ半分の回数である。

ならば、250自体についても同じようにやってみる。

50=(225)2

だから、掛算回数は、27回となる。

25=(212)2×2

とすると、掛算回数は、12+1+1+1+1=16回。これをずっと押し進めると、

100=(((((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 で計算が可能である。

modpow(p, n, m)

のかわりに、pを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 列の計算でも使用する。


この章の目次

E-mail : kc2h-msm@asahi-net.or.jp
三島 久典