Miller-Rabin 素数判定と Pollard のρ法
最近 Project Euler の問題を解いているのだけど、使い回せるコードを探すのが大変になってきたのでここにまとめておくことにする。
前に他のプログラム中で使ったものだが、とりあえず Miller-Rabin 素数判定と Pollard のρ法(またはモンテカルロ法)による因数分解と、このふたつを使った素因数分解のコードを多少修正したもの。言語は Ruby。
# Miller-Rabin 法による素数判定 class Miller_Rabin class << self def mod_pow(b, e, n) x = 1 loop do x = x * b % n if e.odd? return x if (e >>= 1) == 0 b = b ** 2 % n end end def init_sd @s, @d = 0, @n >> 1 while @d.even? do @s += 1 @d >>= 1 end end def witness?(x) x = mod_pow(x, @d, @n) return true if x == 1 || x == @n - 1 @s.times.any? do x = x ** 2 % @n x == @n - 1 end end def probably_prime?(n, k = 20) @n = n return true if @n == 2 || @n == 3 return false if @n < 2 || @n.even? init_sd k.times.all? {witness?(rand(@n - 3) + 2)} end end end # Pollard のρ法による因数分解(Brent のアルゴリズムを使用) class Rho class << self M = 64 def f(x) (x ** 2 + @c) % @n end def init_c begin @c = rand(@n - 1) + 1 end while @c == @n - 2 end def brent k = 1 x = y = rand(@n) loop do k.times {x = f(x)} m = [k, M].min (k / m).times do prod = m.times.inject(1) do |q| x = f(x) q * (x - y) % @n end g = @n.gcd(prod) return g if g > 1 end y = x k <<= 1 end end # 非自明な因数をひとつ返す def factor(n) @n = n loop do init_c q = brent return q unless q == @n end end end end # 素因数分解 class PF class << self def factorize(n) return if n == 1 return @d[n] += 1 if Miller_Rabin.probably_prime?(n) q = Rho.factor(n) factorize(q) factorize(n / q) end # Miller-Rabin 法とρ法により素因数分解を求める def prime_division(n) @d = Hash.new(0) factorize(n) @d.sort end end end # 以下使用例 # フェルマー数 F_6 F6 = 2 ** (2 ** 6) + 1 # 素数判定 p Miller_Rabin.probably_prime?(F6) # 証人数を10として素数判定 p Miller_Rabin.probably_prime?(F6, 10) # 非自明な因数をひとつ見つける p Rho.factor(F6) # 素因数分解 p PF.prime_division(F6)