C 問題で整数系のアルゴリズムを確認する問題を出すの良さそうやね。
問題概要
2 以上の整数 が与えられる。 以上の最小の素数を求めよ。
制約
素数判定
整数 が素数かどうかを判定するのは、整数論的アルゴリズムで最初くらいに学ぶものですね。まず素数の定義を確認する。
正の整数 が素数であるとは、 が と のみで割り切れることである
素数かどうかを判定する方法は、素数の定義に素直に従えば OK。
- を と順に割っていって
- どこかで割れてしまったら合成数
- ずっと割り切れなかったら素数
という方法。たとえば は、2, 3, 4, 5, 6 で割り切れないので素数。 は 5 や 7 で割り切れてしまうので合成数。
しかしこのままでは 通りを調べる必要があるので の計算量を要してしまう。
素数判定の効率化
実は、たとえば 97 が素数であることを確かめるのに、96 まで試し割りする必要はない。 9 まで試せば十分なのだ。
なぜなら、
- 97 ÷ 2 = 48.5
- 97 ÷ 3 = 32.333...
- 97 ÷ 4 = 24.25
- 97 ÷ 5 = 19.4
- 97 ÷ 6 = 16.166...
- 97 ÷ 7 = 13.857...
- 97 ÷ 8 = 12.125
- 97 ÷ 9 = 10.777...
- 97 ÷ 10 = 9.7
となっていきますが、
- 97 ÷ 9 の時点では、その計算の答えの 10.777... が、割る数の 9 よりも大きいが、
- 97 ÷ 10 の時点で、その計算の答えの 9.7 が、割る数の 10 よりも小さい
という逆転が起こっているのです!!!!!
ここから導きだされる結論は、
もし仮に、97 が 10 以上の整数 で割り切れるのならば、
という風にしたときに、 は よりも小さい。つまり、97 は よりも先に で割り切れているはずなのだ!!!
というわけで、97 が 10 以上の整数で割り切れるようだったら、それ以前に割り切れているはずなので、9 まで試せばよい、というわけだ。
一般には
今回は 97 について考えたが、一般の整数 についても似たことがいえる。具体的には
を で試し割りしていったときに、
となったら打ち切って良い
ということがいえる。つまり の範囲を調べることになるので、計算量は となる。
以上をまとめると、素数判定は以下のように書ける ( の場合は例外処理)。
bool is_prime(long long n) { if (n <= 1) return false; for (long long p = 2; p * p <= n; ++p) { if (n % p == 0) return false; } return true; }
今回の問題に戻って
今回の問題は、 と順に素数かどうかを調べていって、素数だとわかったらそれをリターンすれば OK。
ちなみに、 をどこまでインクリメントすればよいのだろう...という不安が当然頭をよぎるのだけど、100003 が素数なので心配ない。 なので、最悪時でも 100003 で止まる。
#include <iostream> using namespace std; bool is_prime(long long n) { if (n <= 1) return false; for (long long p = 2; p * p <= n; ++p) { if (n % p == 0) return false; } return true; } int main() { long long X; cin >> X; long long res = X; while (!is_prime(res)) ++res; cout << res << endl; }