质数(素数)生成之“埃氏筛法”

85 阅读1分钟

题目:给出一个整数 N,生成 [1, N] 之间的全部质数。 埃氏筛法:

  1. 埃氏筛法由古希腊数学家埃拉托斯特尼发明。该方法通过不断地标记当前质数的所有倍数为合数,从而取得最小的未标记整数为下一个质数。每次取最小的质数做如上操作,直到达到设置的“上界”。
  2. 算法示例:
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;
}();
  1. “上界”为什么是 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 一定被标记过。

  2. 每次 j 为什么从 i * i 开始,而不是 2 * i ?

    因为 2 * i < 3 * i < ... < i * i,前面的 2 * i、3 * i ...已经在遍历 2、3 的时候标记过了。因此访问 i 时,可以直接从 i * i 开始标记。