算法笔记 / A LEAF IN THE GARDEN
素性筛选
算法笔记 · 算法理论 / 数论。来自 Daily Learning 的学习笔记。
介绍
通过实时判断或预处理,筛选出素数的方法。
暴力试除法
不断除以比自己小的数,若不能被整除则为素数
步骤
- 从 开始除,直到
- 若 能被 整除,则 不是素数
复杂度
- 时间复杂度
- 空间复杂度
代码
bool is_prime(int n) {
// 0、1不是素数
if (n <= 1) return false;
// 从2试到sqrt(n)
for (int i = 2; i * i <= n; i++) {
if (n % i == 0) return false;
}
// 全都不能整除,是素数
return true;
}
埃拉托斯特尼筛法
维护一个素数标记列,将素数的所有倍数筛掉
设素数集
- (所有素数)
- (所有倍数)
步骤
双层循环
- 筛选掉所有
- 外层遍历 ,内层遍历
- 被外层筛掉的 不会被纳入内层遍历
代码
// 素数集
vector<int> prime;
// 素数标记列
vector<bool> is_prime;
void filter(int n){
// 全部设为素数
is_prime.resize(n, true);
// 0、1不是素数
is_prime[0] = is_prime[1] = false;
// 遍历所有数
for(int p = 2;p < n;p++){
// 当前是素数
if(is_prime[p]){
// 记录素数
prime.emplace_back(p);
// 筛掉倍数
for(int t = 2;t * p < n;t++)
is_prime[t * p] = false;
}
}
}
欧拉筛法(线性筛)
维护一素数列,将素数列中的每个素数的每个倍数筛掉
设素数集
- (所有素数)
- (所有倍数)
步骤
- 筛选掉所有
问题
有些数被筛选两次
举例
10出现了两次!
这是因为:
- 当存在
- 满足 时,
- 会导致 ,
- 该数被筛掉两次
循环逻辑
- 外层遍历 ,内层遍历 ,外层边遍历边添加素数
- 遍历到 时, 还没有被添加到 数列, 不起作用
- 遍历到 时, 已经被添加到 数列, 起作用
- 只筛选一次
跳出时机
- 时,跳出循环
要筛掉的数超过了搜索范围
- 时,跳出循环
是 的因数,
- 设,取
- ,
- 该数已经被 筛掉了,不需要再被 筛选
代码
// 素数集
vector<int> prime;
// 素数标记列
vector<bool> is_prime;
void filter(int n){
// 全部设为素数
is_prime.resize(n, true);
// 遍历所有倍数t
for(int t = 2;t < n;t++){
// 记录素数
if(is_prime[t]) prime.emplace_back(t);
// 遍历所有素数p
for(int p: prime){
// 越界跳出循环
if(t * p > n) break;
is_prime[i * p] = false;
// 已被筛掉跳出循环
// 这时候正好p是最小质因子
if(t % p == 0) break;
}
}
}