埃拉托斯特尼筛法

考虑这样一件事情:对于任意一个大于 的正整数 ,那么它的 倍就是合数 。利用这个结论,我们可以避免很多次不必要的检测。

如果我们从小到大考虑每个数,然后同时把当前这个数的所有(比自己大的)倍数记为合数,那么运行结束的时候没有被标记的数就是素数了。

void eratosthenes(int n) {
    memset(isPrime, 1, sizeof(isPrime));
    isPrime[1] = false;
    for (int i = 2; i <= n; i++)
        if (isPrime[i]) {
            Prime[cnt++] = i;
            for (int j = 2 * i; j <= n; j += i)
                isPrime[j] = false;
        }
}

线性筛法

埃氏筛法仍有优化空间,它会将一个合数重复多次标记。如果能让每个合数都只被标记一次,那么时间复杂度就可以降到 了。

void eulerSieve(int n) {
    memset(isPrime, 1, sizeof(isPrime));
    isPrime[1] = false;
    for (int i = 2; i <= n; i++) {
        if (isPrime[i])
            prime[cnt++] = i;
        for (int j = 0; j < cnt && (long long)i * prime[j] <= n; j++) {
            isPrime[i * prime[j]] = false;
            if (i % prime[j] == 0)
                break;
        }
    }
}

在线性筛中,每一个合数 都是被最小的质因子 筛掉。

当出现 时,如果继续循环筛下去, 会被筛两次;

我们选择 来筛,使得每个数只被筛一次。

例如,

graph LR
2-->|2|4
3-->|2|6
3-->|3|9
4-->|2|8
5-->|2|10
5-->|3|15
6-->|2|12
7-->|2|14
8-->|2|16
9-->|2|18
10-->|2|20
11
13
17
19