在计算素数的过程中,有两种经典的筛法:埃拉托色尼筛法(简称埃筛)和线性筛法(简称线筛)。它们都是用于在给定的范围内找到所有素数的高效算法。本文将通过示例代码,详细对比这两种方法的原理、实现及其时间复杂度。
1. 埃拉托色尼筛法
原理概述
埃拉托色尼筛法的基本思想是通过不断标记合数来筛选出素数。具体来说,对于每一个素数 p,从 p*p 开始标记所有的倍数为非素数,因为更小的倍数已经在之前被标记过。
代码实现
以下是埃筛法的 C++ 实现:
#include <bits/stdc++.h>
using namespace std;
int main(){
ios::sync_with_stdio(false);
cin.tie(NULL);
int n;
cin >> n;
vector<int> isPrime(n + 1, 1); // 初始化所有数为素数
isPrime[0] = isPrime[1] = 0; // 0 和 1 不是素数
for (int i = 2; i * i <= n; i++) { // 从 2 开始到 sqrt(n)
if (isPrime[i]) { // 如果 i 是素数
for (int j = i * i; j <= n; j += i) { // 标记 i 的所有倍数为非素数
isPrime[j] = 0;
}
}
}
// 输出所有素数
for (int i = 0; i <= n; i++) {
if (isPrime[i]) {
cout << i << " ";
}
}
return 0;
}
解析
-
时间复杂度:埃拉托色尼筛法的时间复杂度为 O(nloglogn)。虽然对于每个素数 p,我们需要从 p*p开始标记所有倍数,但通过跳过非素数的倍数,算法仍然具有非常好的效率。
-
空间复杂度:我们使用了一个大小为 n+1的数组来存储每个数是否是素数,因此空间复杂度为 O(n)。
输出示例
对于 n=30,输出结果为:
2 3 5 7 11 13 17 19 23 29
优缺点
- 优点:
- 实现简单。
- 适用于中等范围的素数筛选。
- 缺点:
- 存在重复标记。例如,6 既是 2 的倍数也是 3 的倍数,因此会被多次标记。
- 时间复杂度虽然是 O(nloglogn),但不是最优的。
2. 线性筛法
原理概述
线性筛法通过优化标记过程,确保每个合数只会被它的最小素数因数标记一次,从而避免了重复标记。与埃拉托色尼筛法不同,线性筛法可以严格保持线性时间复杂度 O(n)。
代码实现
以下是线性筛法的 C++ 实现:
#include <bits/stdc++.h>
using namespace std;
int main(){
ios::sync_with_stdio(false);
cin.tie(NULL);
int n;
cin >> n;
vector<int> prime;
vector<int> isPrime(n + 1, 1); // 初始化所有数为素数
isPrime[0] = isPrime[1] = 0; // 0 和 1 不是素数
for (int i = 2; i <= n; i++) { // 从 2 遍历到 n
if (isPrime[i]) { // 如果 i 是素数
prime.push_back(i); // 将 i 加入素数列表
}
for (int j = 0; j < prime.size(); j++) {
int p = prime[j];
if (p * i > n) break; // 如果 p * i 超过了 n,停止标记
isPrime[p * i] = 0; // 标记 p * i 为非素数
if (i % p == 0) break; // 如果 i 能被 p 整除,停止标记
}
}
// 输出所有素数
for (int i = 0; i < prime.size(); i++) {
cout << prime[i] << " ";
}
return 0;
}
解析
-
时间复杂度:线性筛法的时间复杂度为 O(n),因为每个数 i 只会被其最小素数因数标记一次,避免了重复标记。
-
空间复杂度:与埃拉托色尼筛法类似,空间复杂度也是 O(n)。
输出示例
对于 n=30,输出结果为:
2 3 5 7 11 13 17 19 23 29
优缺点
- 优点:
- 时间复杂度为 O(n),更高效,特别是在处理大规模数据时效果显著。
- 每个合数只被最小素数标记一次,避免了不必要的重复操作。
- 缺点:
- 实现稍微复杂,需要维护素数列表并处理标记逻辑。
3. 对比分析
4. 总结
- 埃拉托色尼筛法是一个较为经典的素数筛选算法,适合中小规模的数据筛选,具有简单易懂的实现。
- 线性筛法在进一步优化了筛选过程,能够在线性时间内完成素数筛选,特别适合大规模数据的处理。