エラトステネスのふるいとその計算量

エラトステネスの篩(ふるい)とは,nn 以下の素数を全て見つけ出す高速な方法です。

エラトステネスのふるいの概要と,愚直に計算するよりも速いこと(計算量が O(nloglogn)O(n\log\log n) であること)を紹介します。

愚直な方法

エラトステネスのふるいの前に,まずは愚直な方法で nn 以下の素数を全て列挙することを考えてみます。

愚直な方法
  • 11 から nn まで順々に素数かどうか判定する。

  • kk が素数かどうかは k\sqrt{k} 以下の素数たちで割り算すればよい。

k\sqrt{k} が登場したのは kk が合成数なら必ず k\sqrt{k} より小さい素因数を持つからです。

例えば 3131 が素数かどうか判定するときに 5<31<65 <\sqrt{31} <6 なので 55 以下の素数で割り切れないことを証明すればOKです。つまり「22 の倍数でない,33 の倍数でない,55 の倍数でない」ことを確認すればOKです。

エラトステネスのふるい

エラトステネスのふるい
  1. 22 から nn までの整数を並べる。

  2. 生き残っている数の中で一番小さい(かつまだ pp として使われていない)ものを新たに pp とおき,pp 以外の pp の倍数を全て消していく。

  3. 2の操作を繰り返していき,ppn\sqrt{n} を越えたら終了。最終的に生き残ったものが素数。

n=30n=30 で実験してみます。

n=30の場合の例
  • 22 から 3030 までの整数を並べる。 エラトステネスのふるい1

  • p=2p=2 の倍数をふるい落とす エラトステネスのふるい2

  • p=3p=3 の倍数をふるい落とす エラトステネスのふるい3

  • p=5p=5 の倍数をふるい落とす エラトステネスのふるい4

  • 次は p=7p=7 となるが 30\sqrt{30} より大きいので終了。 3030 以下の素数は,ここまで生き残った 2,3,5,7,11,13,17,19,23,292,3,5,7,11,13,17,19,23,29 である!

合成数は pp として選ばれる前にふるい落とされるので,pp として選ばれるのは素数のみです。つまり「n\sqrt{n} 以下の素数」回だけ上記のふるい落とす操作を行うことになります。

エラトステネスのふるいの計算量

エラトステネスのふるいによる計算量を考えてみます。ただし,ここで言う計算量とは nn が大きいとき,アルゴリズムの遂行に必要な基本演算(四則演算など)の回数を大雑把に評価したものです。定数倍くらいの違いなどは無視して)大雑把に f(n)f(n) であるような量を O(f(n))O(f(n)) と書きます。→オーダー記法(ランダウの記号)の定義と大雑把な意味

計算量の導出

以下のように実装した場合を考える。

  1. 要素数 nn ですべての要素が 11 である配列 AA を用意しておく
  2. AA のすべての 22 の倍数番目の要素に 00 を代入する
  3. AA のすべての 33 の倍数番目の要素に 00 を代入する
  4. AA のすべての 55 の倍数番目の要素に 00 を代入する
  5. n\sqrt{n} 以下の素数まで続ける
  6. 最後に AA の各要素を見て 11 であるのが何番目かを列挙する

手順1と手順6の計算量は O(n)O(n) である。残りの手順については,22 でふるいおとすのに n2\dfrac{n}{2} 回,33 でふるい落とすのに n3\dfrac{n}{3} 回,55 でふるい落とすのに n5\dfrac{n}{5} 回,\cdots の定数時間の演算(配列への代入)を行うので計算量は,

p<nnp=n2+n3+n5+\displaystyle\sum_{p <\sqrt{n}}\dfrac{n}{p}=\dfrac{n}{2}+\dfrac{n}{3}+\dfrac{n}{5}+\cdots

(ただし,和は n\sqrt{n} 以下の素数全体に関して取る)

ここで nn が十分大きいとき n\sqrt{n} までの素数の逆数和はだいたい loglogn=loglogn+log12\log\log \sqrt{n}=\log\log n+\log \dfrac{1}{2} くらい(→素数の逆数和が発散することの証明)なので,計算量は O(nloglogn)O(n\log\log n) となる。

愚直な方法による計算量との比較

最初に紹介した愚直な方法による計算量も求めてみます。

計算量の導出

kk が素数かどうかを判定するのに最大 k\sqrt{k} 回くらいの割り算が必要なので,全体の計算量はおおよそ k=1nk\displaystyle\sum_{k=1}^n\sqrt{k}

これは nn が大きいとだいたい 0nxdx=23nn\displaystyle\int_0^n\sqrt{x}dx=\dfrac{2}{3}n\sqrt{n}

よって,計算量は O(nn)O(n\sqrt{n})

O(nloglogn)O(n\log\log n)O(nn)O(n\sqrt{n}) とでは nn が大きいとき圧倒的に前者の方が計算量が少なくなります(対数関数でさえ増加が遅いのだから loglogn\log\log n はもっと遅い!)。エラトステネスのふるいの素晴らしさがなんとなく分かっていただけたかと思います。

なお,素数定理とかを使えば愚直な方法に対してもう少し計算量を厳しく評価できますが,それでもエラトステネスのふるいの方が速いです。

素数の逆数和が出てくるのは楽しいですね。

Tag:素数にまつわる覚えておくべき性質まとめ