🧹

埃氏筛与线性筛

用一个筛子,把 2 到 n 的质数全部筛出来

💡 打个比方

埃氏筛(Eratosthenes sieve)就像过筛子:把 2~30 的数字站成一排,先拿 2 当筛子,把所有 2 的倍数划掉;再拿 3 当筛子,划掉 3 的倍数;一直筛到 √n。没被划掉的,就是质数。

线性筛是升级版:规定每个合数只允许被它的最小质因子划一次。比如 12 只能被 2 划,不能 3 也来划一遍。谁也不重复干活,速度直接快到 O(n)。

两个筛子:埃氏筛 O(n log log n)——从 p×p 开始划 p 的倍数;线性筛 O(n)——维护质数表,每个合数恰好被最小质因子筛一次,关键在 i % primes[j] == 0 就 break。

🖼️ 大图看明白

① 埃氏筛:拿质数当筛子划倍数,剩下的就是质数(示例 2~30) 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 质数(没被划掉) 被 2 划掉 被 3 划掉 被 5 划掉 ② 线性筛:每个合数只被"最小质因子"划一次,绝不重复 4 ← 被 2 划 6 ← 被 2 划 9 ← 被 3 划 10 ← 被 2 划 12 ← 被 2 划 15 ← 被 3 划 21 ← 被 3 划 25 ← 被 5 划 27 ← 被 3 划 30 ← 被 2 划 12 只被 2 划(它的最小质因子),3 不来抢功;每个合数恰好被划一次 → O(n)

📏 埃氏筛,只要 3 步

  1. 第 1 步:准备名单。开一个 bool 数组 isPrime[2..n],先全部认为是质数(isPrime[0] = isPrime[1] = false)。
  2. 第 2 步:从 2 开始筛。对 i = 2 到 √n:如果 isPrime[i] 还是 true,就从 i × i 开始,把 i 的倍数 i×i、i×(i+1)、… 全部标记为合数(i×2、i×3 那些早被更小的质数划过了)。
  3. 第 3 步:收剩下的。筛完后,isPrime[i] 为 true 的 i 就是 2~n 里全部质数。
线性筛的关键一行:if (i % primes[j] == 0) break; 意思是"i 已经被质数 primes[j] 整除,那 i × primes[j] 之后轮到更小的 primes 来管",保证每个合数只被最小质因子划一次。埃氏筛 O(n log log n),线性筛 O(n)。

💻 C++ 怎么写

// 线性筛:O(n) 求出 2 ~ N 的所有质数
const int N = 1000000;
bool isComp[N];            // true 表示合数(被划掉)
int primes[N], cnt = 0;    // 质数表

for (int i = 2; i <= N; i++) {
    if (!isComp[i]) primes[cnt++] = i;   // i 没被划,是质数
    for (int j = 0; j < cnt; j++) {
        if (1LL * i * primes[j] > N) break;
        isComp[i * primes[j]] = true;    // 用质数表划 i 的倍数
        if (i % primes[j] == 0) break;   // 最小质因子划完,停
    }
}
cout << "质数个数: " << cnt << endl;   // N=1e6 时是 78498
考点提醒:① 埃氏筛从 p×p 开始划,不是从 2p;② 线性筛 break 条件是 i % primes[j] == 0;③ 数组用 bool,开全局避免超栈;④ i × primes[j] 可能超 int,写 1LL 防溢出。

✋ 你来试试

题 1:埃氏筛为什么可以从 i × i 开始划,而不是从 2 × i 开始?
因为 i × 2、i × 3 … i × (i-1) 这些数,都含有比 i 更小的因子,早就被更小的质数划掉了。比如筛 5 时,10、15、20 分别早被 2、3、4(其实是 2)划掉了。从 i×i 开始,一个都不重复,省下大量功夫。
题 2:线性筛中,为什么 i % primes[j] == 0 时要 break?
为了保证每个合数只被它的最小质因子划一次。比如 12 = 3 × 4:当 i = 4、primes[j] = 3 时已经划了 12,如果继续,i = 6、primes[j] = 2 时又会划一次,重复劳动。break 的意思是:"12 这种数以后由更小的质因子 2 来负责(6 × 2)",谁负责、就只让谁划。
🎯 一句话记住:埃氏筛从 p×p 开始划倍数,O(n log log n);线性筛 i % p == 0 就 break,每个合数只被最小质因子划一次,O(n) 出全部质数。