素因数分解(1) Pollardのρ法
RSA暗号が素因数分解の難しさを安全性の根拠にしていることからわかる通り、大きな数の素因数分解は非常に難しいです。
今のところ、200桁程度の数の素因数分解が限界のようです。
素因数分解(1) Pollardのρ法 - wacchoz’s note(今ここ)
素因数分解(2) p-1法、p+1法、楕円曲線法 - wacchoz’s note
素因数分解(3) フェルマー法、2次ふるい法 - wacchoz’s note
素因数分解アルゴリズムはいくつかの方法が知られています。
ざっくり分けると
の2つのカテゴリーになりますが、今回紹介するPollardのρ法は、どちらにも属しません。
Pollardのρ法は、1975年にポラードが考案した方法です。古典的な方法で、ある程度小さい素因子をもつ場合にしか適用できないものの、他のアルゴリズムとは一風変わった面白い手法です。
ポラード・ロー素因数分解法 - Wikipedia
まず素数を適当に1つとり、
と数列を定義すると、は疑似乱数のように振る舞います。
の数式自体にあまり意味はなく、他の数式でも疑似乱数のように振る舞うものであれば、何でもOKです。
はからの値をとるため、いつか同じ値をとり、その後、周期的に変化することになります。
例として、としてみると、
となり、12項目と5項目が一致しました。この後は周期7で繰り返すことになります。
100000までの素数で、何項目で初めて一致する値が出現するかをプロットしてみます。
プロットしてみると、はからの値をとりうるにもかかわらず、かなり早めに一致する値が出現することがわかります。
pが10^5ぐらいでも、数100項目で一致する値が出現します。
これに似た話は誕生日のパラドックスとしてよく知られています。
ランダムに23人を集めてきたら、その中に同じ誕生日の組がいる確率は50%を超え、70人いれば99.9%にも達します。
一般的には、通りの値をとる乱数があるとき、個の中に同じ値が出現する確率が50%を超えるのは、
で、の値に比較して、かなり少ない個数で一致します。
さて、今までは素数を法とした計算としていたが、素因数分解したい数を法とした計算をしてみます。
として、を計算してみます。
mod 25283の数列について、mod 131で考えると左側の数列と一致するので、9131と3236はmod 131で合同になるはずです。
実際、9131-3236は131で割りきれますし、も131で割り切れるため、
となっています。そこで、
「合成数が与えられたとき、を計算時に、ととの最大公約数を計算し、1でなければ終了。1であれば次のkに進む」
という方法が思いつきます。この方法は素因数をとすると、のオーダーまでにかなりの確率で素因子が見つかりますが、計算量がに比例するため、結局のところ、に比例する計算量となり、試し割り算と変わりません。
もっと効率的に計算するために、フロイドの循環検出法という手法を用います。
フロイドの循環検出法 - Wikipedia
あるから先はとなったとします。つまり数列のある箇所から先が周期になったということです。
このとき、になるようにを大きくとると
となるため、とおくと
が成り立ちます。
ちなみに下図がρ法の名前の由来。
よってとなる可能性が高く、そうなれば素因子が見つかったことになります。
の値はわからないため、小さい順にを計算し、との最大公約数をとります。
具体的には、2系統の数列とを生成し、順にを計算すればよいです。
計算量は平均的にはに比例となるため、でおさえられます。
def gcd(a, b): if a<b: return gcd(b,a) r = b while a%b!=0: r = a%b a,b = b, r return r def f(x): return x*x+1 N = 2**(2**6)+1 print(N) x=2 y=2 for i in range(10**7): x = f(x) % N y = f(f(y)) % N g = gcd(x-y, N) if 1<g<N: print(g) break else: print("Fail")