- 学术
线性筛
- @ 2025-10-25 13:31:16
【数论、线性筛】实现一个素数筛
题意
给定一个正整数 ,要求找出从 到 范围内的所有素数。 数据范围:。
题目分析
本题的目标是求解一个指定范围内的所有素数,是典型的素数筛问题。经典的埃拉托斯特尼筛法(Sieve of Eratosthenes)通过对每个素数 标记其所有倍数()为合数来实现,其时间复杂度为 。该算法的瓶颈在于一个合数可能被其多个不同的素因子重复标记。例如, 会被 和 分别标记一次。为了达到线性时间复杂度,我们引入欧拉筛法(Euler's Sieve),也称线性筛。其核心思想是确保每一个合数都只被其最小的素因子筛选(标记)一次。
我们维护一个布尔数组 , 为真表示 是合数。同时,我们用一个数组 存储已经找到的素数。我们从 到 遍历所有数字 。
- 如果 为假,说明 没有被任何比它小的数的素因子筛过,因此 是一个素数,我们将其加入素数数组 。
- 接着,我们遍历已找到的素数数组 。对于每一个素数 ,我们将 标记为合数,即 。这是因为 显然是一个合数,且它的一个素因子是 。
- 最关键的一步是保证每个合数只被其最小素因子筛掉。当我们遍历素数 时,如果发现 是 的倍数,即 ,我们必须立即停止对当前 的进一步筛选。这是因为 是 的最小素因子(如果不是,那么 在早先的步骤中就已经被一个更小的素因子处理,并触发了 break)。对于任何一个比 更大的素数 (),合数 的最小素因子是 (因为 , 所以 ),而不是 。根据我们的核心思想,这个合数 应该由 来筛。它会在后续外层循环遍历到 时,被内层循环的 筛掉。因此,为了避免重复筛选,我们在此处
break。
通过上述机制,我们保证了每个合数 都唯一地被其最小素因子 以 的形式筛掉。外层循环变量为 时,内层循环遍历到 时完成筛选。由于每个数作为 和 的组合只被访问常数次,总时间复杂度为 。
C++ 代码实现
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int N = 1e7 + 5;
int n;
int p[N]; // 存储素数
bool v[N]; // v[i] = true 表示 i 是合数
int cnt; // 素数的数量
void sve(int num) {
for (int i = 2; i <= num; ++i) {
if (!v[i]) {
p[cnt++] = i; // i 是素数
}
// 遍历已有素数来筛掉合数
for (int j = 0; j < cnt && i <= num / p[j]; ++j) {
v[i * p[j]] = true; // i * p[j] 是合数
// 关键:保证每个合数只被其最小质因子筛一次
if (i % p[j] == 0) {
break;
}
}
}
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
sve(n);
// 打印找到的素数数量
cout << cnt << endl;
// 打印所有素数
// for (int i = 0; i < cnt; ++i) {
// cout << p[i] << " ";
// }
// cout << endl;
return 0;
}
0 条评论
目前还没有评论...
京公网安备11010802045784号
YIZHIYANG 一只羊 LV 8