题目:给出一个整数 N,生成 [1, N] 之间的全部质数。 埃氏筛法:
- 埃氏筛法由古希腊数学家埃拉托斯特尼发明。该方法通过不断地标记当前质数的所有倍数为合数,从而取得最小的未标记整数为下一个质数。每次取最小的质数做如上操作,直到达到设置的“上界”。
- 算法示例:
const int N = 100001;
bool isPrime[N]; // isPrime[] 初始化为全 true,算法执行完仍为 true 的索引对应的数字为质数.
int init = []() {
// 埃氏筛
fill(begin(isPrime), end(isPrime), true);
isPrime[1] = false;
for (int i = 2; i * i < N; i++) {
if (isPrime[i]) {
for (int j = i * i; j < N; j += i) { // j 从 i * i 遍历到 N,j 始终是 i 的倍数,因此 j 都是合数,标记为 false.
isPrime[j] = false;
}
}
}
return 0;
}();
-
“上界”为什么是 i * i < N ?
我们把上界设置为 i * i < N,就是说明我们确信不存在 sqrt(N) <= x < N,且 x 未被标记过。(因为如果 x 是合数,但是我们没有标记,就出错了) 我们做个假设,如果存在 sqrt(N) <= x < N,且 x 是合数。那么一定存在 a * b = x,且 a < sqrt(N),b < sqrt(N). 那么 a, b 一定作为 i 被遍历过,它们的倍数也被标记过,也就是说 x 一定被标记过。
-
每次 j 为什么从 i * i 开始,而不是 2 * i ?
因为 2 * i < 3 * i < ... < i * i,前面的 2 * i、3 * i ...已经在遍历 2、3 的时候标记过了。因此访问 i 时,可以直接从 i * i 开始标记。