このアルゴリズムでは, $1$ から $n$ までの整数から
の形に表わすことができる整数を取り除きます. この形で表わせる任意の整数を $k$ とし $2k + 1$ とすれば, 奇数の合成数が得られます (後述). これがサンダラムの篩の核心になります.
素数と合成数
$n > 1$ の整数は次の二種類に分かれます.
素数 $1$ と $n$ 以外の約数がない (例えば $2, 31, 73$ など).
合成数 $1$ と $n$ 以外の約数がある (例えば $8, 49, 72$ など).
また合成数は次の三種類に分かれます.
偶数の合成数 偶数と偶数の積 (例えば $2 \times 4 = 8$ など).
偶数の合成数 偶数と奇数の積 (例えば $8 \times 9 = 72$ など).
奇数の合成数 奇数と奇数の積 (例えば $7 \times 7 = 49$ など).
したがって整数 $n > 1$ は, 素数または合成数で, 合成数だけを篩い落せば素数が残されるという訳です.
理論的な根拠
まず正の整数を
のように大きさの順序に並べ, それぞれの整数を $2n+1$ に代入すれば
で, 偶数の合成数だけを取り除いた数列が得られます. 残ったのは素数と奇数の合成数からなる数列で, あとは奇数の合成数を取り除くことができれば, 素数だけが残る数列が得られることになります.
さて奇数の合成数は
の形で表わすことができます. ここで
とすれば
で
を得ます. 言い換えれば $k$ を
のように大きさの順序に並べ, それぞれの整数を $2k + 1$ に代入すれば奇数の合成数が得られます.
いま正の整数を
のように大きさの順序に並べ, それらの中から $k$ を取り除けば
で, $k$ を篩った数列が得られます. この数列のそれぞれの整数を $2n+1$ に代入すれば
が得られます. ここで $k$ を $2n+1$ にしたと仮定すれば, それは奇数の合成数になるので, 最後に得られた数列は偶数の合成数と奇数の合成数を篩った数列, すなわち素数の数列 (ただし $2$ を除く) となります.
C による実装
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define MAX_PRIMES 1000000
void
sundaram(void)
{
int *sieve = (int *) calloc(MAX_PRIMES/2, sizeof(int));
if (sieve == NULL)
return;
int i, j, k;
for (i = 1; i < sqrt(MAX_PRIMES); i++)
for (j = i; (k = i+j+2*i*j) < MAX_PRIMES/2; j++)
sieve[k] = 1;
printf("2\n");
for (i = 1; i < MAX_PRIMES/2; i++)
if (sieve[i] == 0)
printf("%d\n", 2*i+1);
free(sieve);
}
int
main(void)
{
sundaram();
return EXIT_SUCCESS;
}
実行例
$ time ./a.out | wc -l
664579
0m1.11s real 0m1.06s user 0m0.02s system
$
Cython による実装
from libc.stdlib cimport calloc, free
cdef enum:
MAX_PRIMES = 1000000
cdef sundaram():
cdef bint *sieve = <bint *> calloc(MAX_PRIMES//2, sizeof(bint))
cdef i, j, k
i = 1
while i < MAX_PRIMES ** 0.5:
j = i
k = i + j + 2 * i * j
while k < MAX_PRIMES//2:
sieve[k] = 1
j += 1
k = i + j + 2 * i * j
i += 1
print(2)
i = 1
while i < MAX_PRIMES//2:
if sieve[i] == 0:
print(2*i+1)
i += 1
free(sieve)
sundaram()
実行例
$ time python run.py | wc -l
664579
0m10.96s real 0m10.89s user 0m0.02s system
$
1 コメント :
自然数から奇数の積を取り除く篩法ってやっぱりあるんだな。。。サンダラムの篩か。。。この前検索したときは出てこなかったな。。。
コメントを投稿