火龙信奥
  • 首页
  • 课程
  • 题库
  • 打卡
    • 代码对战
    • 快速对战
  • 题单
  • 团队
  • 荣誉墙
  • 商城
  • 登录 / 注册

数论 - 质数筛法

作者: 作者的头像   huolong , 时间:2022-09-23 15:55:29 , 所有人可见, 阅读  22

筛法

求所有不大于 $n$ 的质数 容易想到对所有不大于 $n$ 的正整数逐个判断是不是质数,但这不是好主意。

调和级数

设调和级数前 $n$ 项的和为 $f(n)$,即 $f(n) = \sum_{i=1}^{n} \frac{1}{i}$

可以证明 $f(n)=O(logn)$

筛法一:朴素筛法

从 $2$ 到 $n$ 枚举整数 $i$,标记大于 $i$ 且不大于 $n$ 的 $i$ 的倍数。枚举到 $i$ 时,若 $i$ 没有被标记过,则 $i$ 为质数。 复杂度为 $O(\sum_{i=1}^{n} \frac{n}{i})=O(nlogn)$

bool vis[N + 1];
vector<int> p;
void sieve() {
    for (int i = 2; i <= N; ++i) {
        if (!vis[i]) p.push_back(i);
        for (int j = i * 2; j <= N; j += i) 
            vis[j] = 1;
    }
}
筛法二:埃氏筛法(埃拉托斯特尼筛法)

从 $2$ 到 $n$ 枚举整数 $i$,若 $i$ 是质数,标记大于 $i$ 且不大于 $n$ 的 $i$ 的倍数。枚举到 $i$ 时,若 $i$ 没有被标记过,则 $i$ 为质数。

可以证明复杂度为 $O(\frac{n}{2} + \frac{n}{3} + \frac{n}{5} + \frac{n}{7} ...) = O(nloglogn)$。这种筛法有个不好记的名字。

bool vis[N + 1];
vector<int> p;
void sieve() {
    for (int i = 2; i <= N; ++i) {
        if (!vis[i]) {
            p.push_back(i);
            for (int j = i * 2; j <= N; j += i) 
            vis[j] = 1;
        }
    }
}
筛法三:欧拉筛法(线性筛法)

从 $2$ 到 $n$ 枚举整数 $i$,再从小到大枚举所有不大于 $i$ 的最小质因子 $p_0$,标记为 $i*p_0$。显然,枚举到的 $p_0$ 总是 $i*p_0$ 的最小质因子,而更大的 $p_0$ 均不可能是 $i*p_0$ 的最小质因子。同样,枚举到 $i$ 时,若 $i$ 没有被标记过,则 $i$ 为质数。

每个合数都只会在其最小的质因子被枚举时被标记,故复杂度为 $O(n)$,这种筛法称为欧拉筛或线性筛。

bool vis[N + 1];
vector<int> p;
void sieve() {
    for (int i = 2; i <= N; ++i) {
        if (!vis[i]) p.push_back(i);
        for (int j = 0; i * p[j] <= N; ++j) {
            vis[i * p[j]] = 1;
            if (i % p[j] == 0) break;
        }
    }
}

重点理解这句话: if( i % prime[j] == 0 ) break;

问题:

1、怎么证明prim[j]就是 i*prime[j]的最小质因子?

分情况讨论: 假如 i%prime[j] == 0 ,那么prime[j]一定是i的最小质数,因为prime[j]是从小到大枚举的,所以它也是 i*prime[j] 的最小质因子。

假如 i%prime[j] != 0 ,那么prime[j] 一定小于 i 的所有质因子,也是因为prime[j]是从小到大枚举的,prime[j] 也是 i*prime[j] 的最小质因子。

证毕。

2、这里为什么就要 break ?

因为 当前的 prime[j] 已经是 i 的最小质因子,如果不及时 break 掉,让循环继续,那么下一个 prime[j+1] * i 的最小质因子还是 prime[j] 而不是 prime[j+1],这是因为i里面含有prime[j]这个质因子,它比prime[j+1]要小(从小到大枚举的) 。

所以要及时 break 。比如 {2,3,5} 当 i = 6时它筛掉是 12,而3*6 = 18不能继续筛,因为18的最小质因子也是2,而不是3.

3、如何保证所有的合数都能被筛掉?

举例合数x,当prime[j]是最小的质因子,当 i 枚举到 x / prime[j] 时候,就可以被筛掉。

每个合数只能被最小的质因子筛掉一次,因此是线性的。

总结: 在 $n$ 是 $10^6$ 左右的两者算法差不多。但在$10^7$线性筛比埃氏筛法快将近1倍。

—— 本文来自火龙信奥(义乌睿码科技):义乌青少年信息学奥赛与编程教育平台,专注 CSP-J/S、NOIP、GESP 竞赛培训,线上线下融合教学,助力编程升学。网址:hlcoding.com

©2026加盟我们 | 关于我们 | ACM课程 | 常见问题 | 成果墙 | 评测记录 | 浙ICP备2021013995号
在线画图 | OI WIki | 打字练习
火龙信奥
请输入登录信息


请完成安全验证
验证码底图 滑块
向右拖动滑块完成验证
请输入用户名 / 绑定的手机号码



请输入注册信息(手机号验证码注册)





验证码5分钟有效,60秒内不可重复获取,每日最多3次

微信登录

微信登录二维码

正在生成二维码...

账号已过期,请续期。
去续期

绑定手机号

📱

为了更好地保护您的账号安全,享受完整的平台服务

请您尽快绑定手机号码