モンゴメリ乗算

提供: testwiki
ナビゲーションに移動 検索に移動

モンゴメリ乗算(モンゴメリじょうざん、テンプレート:Lang-en-short)とは、特に時間のかかる除算を実質的に行うことなく、乗算加算減算シフト演算のみで、効率的に整数の積の剰余を求めることのできるアルゴリズムである。

応用において、特に暗号理論の分野では、数百ビットを超える法による冪剰余演算が重要な役割を果たし、このような冪剰余の演算にモンゴメリ乗算が用いられている。

モンゴメリ乗算の名称は提案者の テンプレート:En に由来する。

モンゴメリ乗算はまた、モンゴメリ法 (テンプレート:Lang-en-short) 、モンゴメリ剰余乗算 (テンプレート:Lang-en-short) とも呼ばれる。

概要

モンゴメリ乗算のアイデアは、N>0 を法とした合同算術に関して、演算したい値を、ある定数 R を掛けた表現(ここではモンゴメリ表現と呼んでおく)に変換し、この表現によってすべての計算を行った後、最後に元の領域での表現に逆変換することである。

モンゴメリ表現での加減算はそのまま実行した後、負または N 以上のときのみ N の加減をするだけでよい。 しかし乗算では R が余分に残るので、R1 を掛けて N による剰余を求める処理を行う必要がある。 この処理をモンゴメリリダクションといい、R をうまく選ぶことにより効率的に計算することができる。

アルゴリズム

モンゴメリリダクション

上述したように、モンゴメリリダクションは、モンゴメリ乗算の基本となる演算である。 N を法とする R に関する T0T<NR )のモンゴメリリダクションは

MR(T)=TR1modN

と定義される。 ここで、RR>N および gcd(N,R)=1 (つまり N互いに素)なる任意の整数であり、R1RR11(modN) なるモジュラ逆数である。

モンゴメリリダクションは次の手続きで計算できるが、R として、modR/R の計算が簡単になるような値(例えば2進数であれば2の冪)を選ぶことにより、除算を実質的に行う必要がなくなり効率的に計算できる。 ここで、NNN1(modR) なる値であり、xRyN=1 を満たす 0<y<R として、拡張されたユークリッドの互除法#Rが2の冪の時のN'の効率的な求め方などであらかじめ求めておく。 同時に得られる 0<x<N は上述の R1 である。

t(T+(TNmodR)N)/R
if tN then return tN else return t

モンゴメリリダクション計算手続きの正当性

この手続きの正当性は次のように示される。 まず、

T+(TNmodR)NT+TNNTT0(modR)

より、/R が割り切れて t が整数であることがわかる。 次に、

tRT+(TNmodR)NT(modN)

より、tTR1(modN) である。 最後に、

T<RN, (TNmodR)N<RN

より、0t=(T+(TNmodR)N)/R<2N であるため、手続きが返す値は N より小さい。

モンゴメリ表現への変換と逆変換

整数 0a<N をモンゴメリ表現 A に変換するためには、R を掛けて N による剰余を求めればよいので、あらかじめ R2=R2modN を用意して、AMR(aR2) を求めればよい。

逆変換はモンゴメリリダクションそのものであり、aMR(A) である。

乗算剰余演算

被乗数 0a<N と乗数 0b<N の乗算剰余 c=abmodN は、上述の変換と逆変換を用いて、

AMR(aR2), BMR(bR2)
CMR(AB)
cMR(C)

により求められる。

しかし、単純に乗算剰余を1回だけ求めたい場合には、

cMR(MR(ab)R2)

とする方が効率的である。

冪剰余演算

冪剰余 akmodN を求めたい場合、まず a をモンゴメリ表現に変換しておき、Ak の計算に出現する乗算のたびに積にモンゴメリリダクションを行っていけば、最後の結果に対して逆変換することによって結果を得ることができる。

通常の冪の計算ではバイナリ法などが効率的であるが、冪剰余の計算においても、同様の効率化がそのまま利用できる。

Rが2の冪の時のN'の効率的な求め方

Rが2の冪である場合には、計算機向けの効率的な求め方が存在する。

NN1(modR)

NNR1(modR)

であり、Rが2の冪であるためR-1は二進数表記で全てのビットが1になる。また2の冪での剰余を求めることは即ち二進数表記での下位ビットを取り出すことである。なのでこれは実質的に、NN'の二進数表記の下位ビット全てが1になるN'を求めることに相当する。

int result = 0;
int t = 0;
int r = R;
int i = 1;

while( r > 1 ) /* Rのトップビットを除いたビット数分繰り返す */
{
  if( !( t % 2 ) ) /* ゼロになっているビットがあったら、N'のその部分を1にする(NはRと互いに素なので必ず奇数) */
  {
    t += N; /* 掛け算だが、二進数一桁の掛け算なので実質は足し算 */
    result += i; /* N'のその部分を1にする */
  }
  t /= 2; /* 必ず端数が出るが切り捨てる */
  r /= 2; /* Rは2の冪なので、絶対端数は出ない */
  i *= 2;
}
/* return result; この時点で N' == result */

参考文献