この記事ではbaillie2021strengtheningの記述を基にBaillie-PSW素数判定法について解説する.Baillie-PSW素数判定法とは,底
この記事での前提知識は合同式のみである.また,最短で素数判定法にたどり着けるようにするため,証明や細かい話は全て省略している.気になる人は参考文献を参照のこと.
プログラムが載っていなかったり,脚注が増えていたりなど一部内容が異なるが,この記事の原稿となった PDF版 のノートも配布する.
整数
最大公約数はユークリッドの互除法を用いて,計算量
def gcd(a, b):
while b:
a, b = b, a % b
return a
たとえば,法
なので,
特に法が奇素数のとき,平方剰余は様々な性質を持つ.このことを表すために次の記法を導入する.
と定義する.この
たとえば,
となる.
ルジャンドル記号は法を奇素数に制限した.それを正の奇数に拡張したものがヤコビ記号である.
と定義する(右辺はルジャンドル記号である).この
アルゴリズムは異なるが,Pythonを使った単純なアルゴリズムの実装例を紹介する.実際に動かしてみるとわかるが,大きい数でも十分高速に動作する.
def jacobi(a, n):
if n <= 0:
raise ValueError("'n' must be a positive integer.")
if n % 2 == 0:
raise ValueError("'n' must be odd.")
a %= n
result = 1
while a != 0:
while a % 2 == 0:
a /= 2
n_mod_8 = n % 8
if n_mod_8 in (3, 5):
result = -result
a, n = n, a
if a % 4 == 3 and n % 4 == 3:
result = -result
a %= n
if n == 1:
return result
else:
return 0
法が素数のとき,フェルマーの小定理(Fermat's little theorem)という有名な定理が成り立つ.
一般にフェルマーの小定理の逆は成立しない.実際,
であるが,
正の奇数
上述の通り,フェルマーテストを潜り抜ける合成数
Pythonでの実装例は次の通りである.
import math
import random
def is_prime(n, KMAX=100):
if n == 1: return False
if n == 2: return True
for k in range(KMAX):
a = random.randint(2, n - 1)
if math.gcd(n, a) != 1:
return False
if pow(a, n - 1, n) != 1:
return False
return True
ミラー・ラビン素数判定法(Miller-Rabin primality test)は次の定理に基づいた確率的素数判定法である.
ミラー・ラビン素数判定法はこの定理の対偶を利用して,合成数を判定する.
正の奇数
フェルマーテストと違って「強(strong)」がついているのは,素数の確率がより高いことを意味する.同様に,素数と誤って判定されてしまう合成数
ミラー・ラビン素数判定法のPythonの実装例を紹介する.
import random
def is_prime(n):
if n == 2: return True
if n == 1 or n & 1 == 0: return False
d = (n - 1) >> 1
while d & 1 == 0:
d >>= 1
for k in range(100):
a = random.randint(1, n - 1)
t = d
y = pow(a, t, n)
while t != n - 1 and y != 1 and y != n - 1:
y = (y * y) % n
t <<= 1
if y != n - 1 and t & 1 == 0:
return False
return True
整数
リュカ数列と素数に対して,次の定理が成り立つ.
が成り立つ.もし,
リュカ数列のパラメータを
式(
正の奇数
リュカの素数判定法で素数と誤って判定される合成数をリュカの擬素数(Lucas pseudoprime)という.また,
リュカの素数判定法におけるパラメータ
この方法を用いた場合,リュカの擬素数は
となる.また,
フェルマーテストからミラー・ラビン素数判定法への強化と同じようにして,次の定理を用いてリュカの強確率的素数と強擬素数を定義する.
正の奇数
リュカの強素数判定法をすり抜ける合成数を,パラメータ
となる.また,
Baillie-PSW素数判定法は,ミラー・ラビン素数判定法とリュカの強素数判定法を組み合わせた手法である.ミラー・ラビン素数判定法における強擬素数と,リュカの強擬素数は異なる数となる傾向がある.すなわち,両方の素数判定法をすり抜ける擬素数は極めて稀ということである.実際,
正の奇数
実用上,ミラー・ラビン素数判定法の前にしきい値
さきほど,Baillie-PSW素数判定法をすり抜ける合成数が極めて稀と述べたが,実際のところは未解決問題である.ヒューリスティックな解析によると,無数に擬素数は存在すると予想されているが不明であるpomerance1984there.もし,反例を見つけた場合は620ドルの賞金が手に入るようだ.ちなみに,文献によって書いている賞金額が違うので,実際にはもっと少ない可能性がある.baillie2021strengtheningでは620ドル,pomerance1984thereでは120ドルと書かれている.また wikipedia によると,620ドルは別の問題に対する賞金だという指摘もある.
Baillie-PSW素数判定法のPythonの実装例は以下のページを参照のこと.愚直に実装するとリュカ数列の計算量が大変なことになるが,ビット表示して効率的に計算できる.
Baillie-PSW/baillie_psw.py at main · armchaircaver/Baillie-PSW (github.com)