この例は、入力した上限 n 以下の素数を試し割りで判定し、1から n まで列挙するC17のプログラムです。素数は2以上の整数なので、上限が0または1なら素数は表示しません。入力には0以上 INT_MAX 以下の10進整数を1個指定します。
コード
| |
コンパイルと実行例
primes.c として保存し、GCCが使える環境で gcc -std=c17 -Wall -Wextra -O2 primes.c -o primes を実行します。続いて ./primes を実行し、上限として 30 を入力すると、見出しに続いて次の行を表示します。
| |
上限が2なら 2、0または1なら空行です。入力はテキストの1行として読み取り、改行を除いて127バイトまで受け付けます。前後の空白や先頭の + は許可しますが、負数、空行、整数以外の文字、範囲外の数値、長すぎる行、読み取り失敗はエラーにして終了します。最初の行だけを処理し、後続の行は使いません。
判定と入力処理の仕組み
isPrimeは1以下を偽、2と3を真とし、それ以外で2または3の倍数なら偽にします。その後はi = 5, 11, 17, ...に対してiとi + 2を調べます。つまり候補は6k - 1と6k + 1であり、6の倍数ではありません。この形の数がすべて素数になるわけでもありません。- 合成数には平方根以下の素因数があるため、
i <= num / iの間だけ調べれば十分です。正の整数ではi * i <= numと同じ範囲を表し、intの乗算のオーバーフローを避けられます。平方根を浮動小数点で計算する必要もありません。 printPrimesは上限が2未満なら空行だけを出力します。それ以外では2を出力した後、奇数を判定します。i > n - 2なら加算の前に終了するので、上限がINT_MAXでも最後のi += 2によるオーバーフローを避けます。readLimitはfgetsで読み、strtolの変換終了位置とERANGE、さらに0からINT_MAXまでの範囲を確認してからintに変換します。isspaceにはunsigned charに変換した値を渡します。scanf("%d", &n)の戻り値を無視する実装は、入力失敗時に未初期化のnを使用するおそれがあります。また、変換結果がintに収まらない入力は、単に変換失敗として扱えるとは限りません。
適用範囲
INT_MAX は環境によって異なります。32ビットの int では2,147,483,647ですが、Cのすべての処理系でこの値になるわけではありません。コードがその値を表現できることと、そこまでの全素数を短時間で表示できることは別です。
これは試し割りを学ぶための実装です。1個の値の判定では平方根に比例する回数を上限とする除算を行い、全候補の列挙では大まかに O(n sqrt(n)) 回の除算を上限として考えられます。これは除算を1操作と数えた上界で、ビット演算量や実測時間ではありません。広い範囲の列挙にはエラトステネスの篩などを検討します。暗号用の素数生成器ではありません。大きな整数の判定については、Miller–Rabin法の記事も参照してください。
Cの入力関数と整数演算の根拠は、WG14のC11委員会草案 N1570の6.5、7.4、7.21.6.2、7.21.7.2、7.22.1.4で確認できます。
