LoginSignup
0
1

n**2+1の素数判定を2次ふるいで行う

Last updated at Posted at 2022-11-15

【例題 1】以下の問題を解け

\begin{align}
&nが2 \le n \le Nの整数の時 \ \ (N=10^7)    \\ 
&f(n) = n^2+1 が素数になる個数を求めよ    \\
\end{align}

pythonではsympyを使えば一行で書け速度もそこそこですがわずかに1分をオーバーしました。今回はこれを2次ふるい法と呼ばれるアルゴリズムを使って解いてみたいと思います。

from sympy import isprime
N = 10**7
print(sum([isprime(n**2 + 1) for n in range(N+1)]))
# 456361

2次ふるい法 (Quadratic sieving)

ここで紹介する「2次ふるい法」は一般に素因数分解で使われるものとは異なり、2次式で「エラトステネスのふるい」のように合成数を除いて行って素数だけが残るようにするアルゴリズムです。詳細は以下のサイトにありますが長文なので、そのさわりだけを紹介します。

Prime sieving for the polynomial f(n)=n2+1

ここで使われているアルゴリズムは以下の性質を利用しています。

  • もし$f(n)=n^2+1$が素数ならばこれを$p$とすると、$f(pk \pm n) (kは任意の整数)$は$p$で割り切れる合成数である
\begin{align}
&【証明】 \\
f(pk \pm n) &= (pk \pm n)^2 + 1 \\
&= p^2k^2 \pm 2pkn + n^2 + 1 \\
&\equiv n^2 + 1 = p \equiv 0 \pmod{p} \\
\end{align}

これで一気に合成数をふるい落とすことができます。例えば、

\begin{align}
&f(2)=2^2+1 = 5 なので、n=2, p=5, \\
&f(5k \pm 2) はすべて5で割れる合成数となり \\
&これらを5で割れるだけ割ります \\
&下の例で、\\
&k=1の時は5-2-3と5+2=7を \\
&k=2の時は10-2=8と10+2=12をnとしたf(n)を \\
&5で割れるだけ割ります。この時これらをは \\
&素数でないと分かるので素数フラグをFalseにします。\\
\end{align}
 f( 1 ) = 2
 f( 2 ) = 5
 f( 3 ) = 10 --> 2 : not prime 
 f( 4 ) = 17
*f( 5 ) = 26
 f( 6 ) = 37
 f( 7 ) = 50 --> 2 : not prime 
 f( 8 ) = 65 --> 13 : not prime 
 f( 9 ) = 82
*f( 10 ) = 101
 f( 11 ) = 122
 f( 12 ) = 145 --> 29 : not prime 

実はこの処理は一石二鳥で以下の2つを同時に行っています。

  1. f(n)が合成数であることが分かる
  2. f(n)を既知の素数で割ることによって、新たな素数が出てくる(f(8)の13, f(12)の29)ので、これらをこの後の処理に利用できる。

この$f(pk \pm n)$は$p$で割る処理を$pk \pm n \le N$で行い、これを$n \le N/2$まで行います。素数フラグがFalseがならなかった$f(n)$が素数であることが分かります。

これをpythonで実装したのが以下になります。実行時間はisprimeを使った場合の半分程度ですが、numbaを使えばその1/10程度になります。

from numba import njit
@njit
def solve(N):
  f = [i**2+1 for i in range(N+1)]
  primes = [1]*(N+1)
  for n in range(1,N//2+1):   # enough to check b< 1/2*N
    if f[n] == 1: continue
    p = pk = f[n]
    #print(p, b)
    while pk-n <= N:
      while f[pk-n] % p == 0: f[pk-n] //= p
      primes[pk-n] = 0
      if pk+n <= N:
        while f[pk+n] % p == 0: f[pk+n] //= p
        primes[pk+n] = 0
      pk += p
  return sum(primes[2:])

N = 10**7
print(solve(N))
# 456361

他の2次式への応用

このアルゴリズムは以下の2次式にもほぼそのまま利用することが出来ます。

\begin{align}
 f(n)&=4n^2+1 \\
 f(n)&=2n^2+1 \\
 f(n)&=2n^2-1
\end{align}

このアルゴリズムは以下のProject Eulerの問題を解くのに役に立ちます。

  • Problem 216: Investigating the primality of numbers of the form $2n^2-1$
  • Problem 659: Largest prime

(開発環境:Google Colab)

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1