GESP五级 质数筛

一、质数筛概述

  • 用途:批量生成≤n的所有质数,效率远高于逐个判断质数
  • 考点:埃拉托斯特尼筛法(埃氏筛)、欧拉筛(线性筛)的代码实现、优化原理、时间复杂度对比

二、埃拉托斯特尼筛法(埃氏筛)

1. 核心原理

从2开始,将每个质数的所有倍数标记为合数,最终未被标记的就是质数。

2. 模板代码

vector<bool> sieve(int n) {
    vector<bool> is_prime(n + 1, true); // 先全部标记是质数
    is_prime[0] = is_prime[1] = false; // 0和1不是质数
    for (int i = 2; i <= n; i++) { // 从2开始标所有质数的倍数
        if (is_prime[i]) { // i是质数
            for (int j = i * i; j <= n; j += i) { // 标记i的倍数
                is_prime[j] = false;
            }
        }
    }
    return is_prime;
}

3. 优化版(从i*i开始)

for (long long j = (long long)i * i; j <= n; j += i) {
    is_prime[j] = false;
}
  • int j = i * i -> long long j = (long long)i * i
  • 优化原因:小于i*i的i的倍数,已经被更小的质因子筛过了(真题第4题考点)
  • 例:i=5时,10、15、20已经被2、3筛过,只需从25开始

4. 时间复杂度

O(n log log n),接近线性,实际运行效率极高


三、欧拉筛(线性筛)

埃氏筛对例如90,会用2、3、5重复筛除,还可以优化。

1. 核心原理

每个合数只会被它的最小质因子筛掉一次,彻底避免重复标记,实现真正的线性时间复杂度。

欧拉筛(线性筛)简化示例表(n=30)

i质数表 primes筛除的数 (i×p)核心逻辑(只筛一次的关键)
2[2]42是质数,筛 2×2;2%2=0 → break
3[2,3]6、93是质数,筛3×2/3×3;3%3=0 → break
4[2,3]8仅筛4×2(最小质因子);4%2=0 → break,不筛4×3=12
5[2,3,5]10、15、255是质数,连续筛;5%5=0 → break
6[2,3,5]12仅筛6×2;6%2=0 → break,不筛6×3=18
7[2,3,5,7]14、217是质数,筛7×2/7×3;7×5=35>30 → 越界break
8[2,3,5,7]16仅筛8×2;8%2=0 → break,不筛8×3=24

只筛一次的核心逻辑

欧拉筛的关键在于if (i % p == 0) break

  • p 能整除 i 时,i 可以写成 p × kpi 的最小质因子),此时 i × p'p' > p)的最小质因子一定是 p,后续会在 i' = k × p' 时由 p 筛除,因此直接 break,避免重复筛除。

比如:

  • i = 4时:12 不会被 4×3 筛除,因为4是2的倍数,4的倍数也一定是2的倍数,后续总能被2给筛除,所以判断4%2==0break了。
  • 同理,18 不会被 6×3 筛除(i=6break),6 * 2筛掉12后,发现6是2的倍数,则后续6的倍数都可以由2在某个时间筛除,即由 9×2 筛除(2 是18的最小质因子)。

2. 完整代码

vector<int> euler_sieve(int n) {
    vector<bool> is_composite(n + 1, false);
    vector<int> primes; // 存储所有质数
    for (int i = 2; i <= n; i++) {
        if (!is_composite[i]) {
            primes.push_back(i); // 未被标记,是质数
        }
        // 内层循环条件:遍历当前素数表 & 标记的数在需求范围内
        for (int j = 0; j < primes.size() && (long long)i * primes[j] <= n; j++) {
            is_composite[i * primes[j]] = true; // 标记乘积为合数
            // 核心优化语句,一旦发现i是当前质数的因数,则break
            if (i % primes[j] == 0) {
                break;
            }
        }
    }
    return primes;
}

3. 时间复杂度

O(n),理论上最优的质数筛法


四、埃氏筛 vs 线性筛(必考对比)

特性埃氏筛线性筛(欧拉筛)
核心思想标记所有质数的倍数每个合数仅被最小质因子标记一次
时间复杂度O(n log log n)O(n)
重复标记会(如12被2、3各标记一次)不会
小规模数据实际速度(n≤1e7)更快(实现简单,常数小)稍慢(逻辑复杂,常数大)
代码复杂度简单中等

坑点

  • ❌ 错误:线性筛一定比埃氏筛快
  • ✅ 正确:n≤1e7时,埃氏筛实际速度通常优于线性筛

五、考点

说法对错说明
线性筛时间复杂度更低,所有情况都应优先使用n≤1e7时埃氏筛实际更快
线性筛每个合数只会被最小质因子筛去一次线性筛的核心原理
线性筛时间复杂度为O(n)无重复标记,线性时间
线性筛通过"每个合数被最大质因子筛去"实现O(n)最小质因子,不是最大
埃氏筛会对同一个合数进行多次标记如12被2、3各标记一次
单个质数判断比筛法效率更高批量生成素数表时,筛法效率远高于逐个判断
  • 埃氏筛从i*i开始的原因:小于i*i的i的倍数已被更小质因子筛过
  • 线性筛的核心break条件:i % primes[j] == 0
  • 线性筛内层循环必须同时满足:j < primes.size()i * primes[j] <= n
  • 批量生成素数表的最优选择:

    • n≤1e7:埃氏筛(实际更快)
    • n>1e7:线性筛(理论复杂度更优)
  • 单个质数判断适合:少量数的质数检测;筛法适合:批量生成素数表

标签: none

评论已关闭