法Nでの逆数を求めるその2

一つ前のエントリ

法Nでの逆数を求める

の手法は途中の計算結果を保存しておかないといけないという問題がありました。そこでそれを改良したアルゴリズムを紹介します。このアルゴリズムもユークリッドの互除法を使うという点では同じです。違うのは

ax+by=1

ではなく

ax+by=N

を考えている点がです。この式は a=N,b=n とすると x=1,y=0 という自明な解を持っています。

そこで a1=N,b1=n,x1=1,y1=0 として前回と同じように

an+1=bnbn+1=mnxn+1=dnxn+ynyn+1=xn と変形していきます。 anxn+bnyn=N が常に成り立っているので、

anxn=Nbnyn が成立します。 an=bn1,yn=xn1 ですから bn1xn=Nbnxn1 と書き直すことができます。nを2からnまで変化させた等式

b1x2=Nb2x1b2x3=Nb3x2bn1xn=Nbnxn1

について左辺どうし、右辺どうしを掛け合わせると

=b1b2bn1x2x3xn =N()±b2b3bnx1x2xn1

となります。 (modN)で考えれば N()は0とみなせるので結局

b1xn=±bnx1(modN)

が常に成立していることになります。符号はnの偶奇で決まります。なお b1,b2,... および x1,x2,...がゼロでないという証明が別途必要ですがそれは最後に記します。

ところで、bnはいずれ1になります。x1=1,b1=nでしたから、その時点で

nxn=±1

となり xnが求める解になります。このアルゴリズムは途中の計算結果を保存しておかなくてよいという利点があり一つ前のエントリより優れていると言えましょう。

サンプルコード


#!/usr/bin/env ruby
    
n = a = 916132831
b0 = b = 9973
x = 1
y = 0
sign = 1

while b != 1
  d = a / b
  m = a % b
  
  a = b
  b = m
  
  tmp = d * x + y
  y = x
  x = tmp
  
  sign = -sign
end

puts("#{sign * x} * #{b0} = #{b0 * x * sign % n} (mod #{n})")

さて、このアルゴリズムでも十分実用になるのですが、Nが奇数の場合はさらに効率よく求めることができます。 そのアルゴリズムを次のエントリで紹介しようと思います。

補足

b1,b2,... および x1,x2,...がゼロでないという証明

bnについては単調に減少し1になったところでアルゴリズムは止まるので0でないことは明らか。 xnについては次の補題を利用します。

補題

gcd(an+1,bn+1)=gcd(an,bn)gcd(xn+1,yn+1)=gcd(xn,yn)

この補題よりgcd(xn,yn)=gcd(x1,y1)=1であり、したがって xn=0ならばyn=1でならねばならず それはbn=0でなければならないので矛盾

Posted by issei

カテゴリ : 数学