D07 エラトステネスの篩
2からnまでの素数を調べていく有名な方法です。
小さい方から倍数を消していく,というごく単純な考え方ですが,アルゴリズムの練習として格好の題材でしょう。
このとき,nまで全部調べなくても,√n まで調べればよいことが知られています。
一般的な手順は
(1) 2からnまでの整数を書く。
(2) 2に丸をつけ、2より大きい2の倍数にバツを付ける。
(3) 以下は、まだ消されていない最小の整数に丸をつけ、その2倍以上の倍数にバツを付ける。
ですが,(3)のかわりに,
(3') 次の整数にバツが付いていなければ丸を付け、その2倍以上の倍数にバツを付ける。
とする方が多いでしょう.「まだ消されていない最小の整数」を探す方法が問題だからです。これを探すのに,つぎの数からバツがついているかどうか調べるのであれば(3')と同じです。
そこで,ひとまず(3')で進めますが,(1)〜(3')をそのとおりまじめにやるかどうか,それによってスクリプトが変わってくるでしょう。
というのは,最初から全部丸にしておいて,倍数をバツにしていっても同じだからです。さらに,(3')もまじめに判断しないで,全部やってしまうことも考えられます。
まずはその手抜きスクリプトです。なお,以下のスクリプトをコピーして実行するときは,各行の先頭に全角スペースが入っているのでこれを削除してください。
< その1 >
sieve(n):=(
res=apply(1..n,"〇");
res_1="×"; // 1は素数ではない
repeat(floor(sqrt(n)),s,start->2,
m=floor(n/s); // sの倍数の個数
repeat(m-1,t,start->2,
res_(s*t)="×";
);
);
res;
);
試しに20まで結果を表示してみましょう。
あとで効率を調べるために処理回数(ループした回数)を記録しておきましょう。
sieve(n):=(
counter=0; // カウンタ
res=apply(1..n,"〇");
res_1="×"; // 1は素数ではない
repeat(floor(sqrt(n)),s,start->2,
m=floor(n/s); // sの倍数の個数
repeat(m-1,t,start->2,
res_(s*t)="×";
counter=counter+1; // ここでカウント
);
);
println("処理回数="+counter); //カウンタ表示
res;
);
100までやると190回でした。
<その2>
sの倍数の個数を求めないで,repeat() の修飾子を用います。stopとstepです。
sieve(n):=(
counter=0; // カウンタ
res=apply(1..n,"〇");
res_1="×"; // 1は素数ではない
repeat(floor(sqrt(n)),s,start->2,
repeat(n,t,start->2*s,stop->n,step->s,
res_t="×";
counter=counter+1; // ここでカウント
);
);
println("処理回数="+counter); //カウンタ表示
res;
);
100までの処理回数はその1とおなじく190回でした。
<その3>
(3') 次の整数にバツが付いていなければ丸を付け、その2倍以上の倍数にバツを付ける。
をまじめにやります。ただし,丸はすでについています。
sieve(n):=(
counter=0; // カウンタ
res=apply(1..n,"〇");
res_1="×"; // 1は素数ではない
repeat(floor(sqrt(n)),s,start->2,
if(res_s!="×", // ここで判断
repeat(n,t,start->2*s,stop->n,step->s,
res_t="×";
counter=counter+1; // ここでカウント
);
);
);
println("処理回数="+counter); //カウンタ表示
res;
);
100までの処理回数は121回でした。