私はMiller-Rabin primality testを実装しようとしていましたが、なぜ中規模の数値 (~7 桁) にこれほど長い (> 20 秒) かかるのか戸惑いました。最終的に、次のコード行が問題の原因であることがわかりました。
x = a**d % n
(ここでa
、 、d
、およびn
はすべて似ていますが、等しくない中規模の数値です。**
は累乗演算子、%
はモジュロ演算子です)
次に、次のように置き換えてみました。
x = pow(a, d, n)
それに比べて、それはほとんど瞬時です。
コンテキストについては、元の関数を次に示します。
from random import randint
def primalityTest(n, k):
if n < 2:
return False
if n % 2 == 0:
return False
s = 0
d = n - 1
while d % 2 == 0:
s += 1
d >>= 1
for i in range(k):
rand = randint(2, n - 2)
x = rand**d % n # offending line
if x == 1 or x == n - 1:
continue
for r in range(s):
toReturn = True
x = pow(x, 2, n)
if x == 1:
return False
if x == n - 1:
toReturn = False
break
if toReturn:
return False
return True
print(primalityTest(2700643,1))
時限計算の例:
from timeit import timeit
a = 2505626
d = 1520321
n = 2700643
def testA():
print(a**d % n)
def testB():
print(pow(a, d, n))
print("time: %(time)fs" % {"time":timeit("testA()", setup="from __main__ import testA", number=1)})
print("time: %(time)fs" % {"time":timeit("testB()", setup="from __main__ import testB", number=1)})
出力 (PyPy 1.9.0 で実行):
2642565
time: 23.785543s
2642565
time: 0.000030s
出力 (Python 3.3.0、2.7.2 で実行すると、非常に類似した時間が返されます):
2642565
time: 14.426975s
2642565
time: 0.000021s
また、関連する質問として、通常は PyPy の方がはるかに高速なのに、Python 2 または 3 で実行すると、PyPy で実行した場合よりもこの計算がほぼ 2 倍速くなるのはなぜですか?