埃氏筛法能否成?
摘要:📘 埃氏筛法(Eratosthenes Sieve)算法笔记 ✨ 目标与背景 我们希望高效求出小于等于 n 的所有质数(素数)。 相比朴素判断法(每个数都除一遍),埃氏筛法大大提高了效率,时间复杂度为 O(n
📘 埃氏筛法(Eratosthenes Sieve)算法笔记
✨ 目标与背景
我们希望高效求出小于等于 n 的所有质数(素数)。
相比朴素判断法(每个数都除一遍),埃氏筛法大大提高了效率,时间复杂度为 O(n log log n),非常适合处理范围较大的质数筛选问题。
✅ 核心思想
筛掉合数,留下质数。
从 2 开始,标记其为质数。
把所有 2 的倍数都标记为 非质数。
找下一个未被标记的数(如 3),认为它是质数,然后再标记它的倍数。
重复直到 √n。
📌 关键实现代码
vector<int> getPrimes(int n)
{
vector<bool> isPrime(n + 1, true); // 标记数组
vector<int> primes;
isPrime[0] = isPrime[1] = false; // 0 和 1 不是质数
for (int i = 2; i <= n; i++)
{
if (isPrime[i])
{
primes.push_back(i); // i 是质数
if ((long long)i * i <= n)
{
for (int j = i * i; j <= n; j += i)
{
isPrime[j] = false; // 标记 i 的倍数为合数
}
}
}
}
return primes;
}
🧠 优化细节说明
✅ for (int j = i * i; j <= n; j += i)
直接从 i² 开始标记,因为比 i² 小的倍数已经被更小的质数筛掉了。
如 2 的倍数 4, 6, 8... 从 2² 开始即可;而 6 = 2*3 已被 2 标记过。
✅ (long long)i * i <= n
避免 int 类型乘法溢出,尤其是当 n 较大时。
🧪 示例:n = 10
初始: [T, T, T, T, T, T, T, T, T, T, T] // 0~10
变成: [F, F, T, T, F, T, F, T, F, F, F]
最终质数:2, 3, 5, 7
💡 时间与空间复杂度
时间复杂度: O(n log log n)
空间复杂度: O(n)(主要用于布尔标记数组)
对比朴素方法(每个数都除一遍),效率提升巨大。可以轻松处理 n ≈ 10^6。
📌 应用场景
筛选范围内所有质数(如欧拉函数、区间素数)
快速构造素数表用于后续操作(如试除法、分解因子)
作为“预处理”模块在很多算法题中非常实用
📝 心得体会
✅ 筛法比起朴素判断法更高效、更优雅,是解决与质数有关问题的标配工具。
✅ 优化起始点到 i*i 是算法的核心思想之一,也是避免冗余标记的关键。
❗ 初次实现要注意防止溢出,建议用 long long 做乘法判断。
✅ 延伸学习方向
欧拉筛(线性筛):进一步优化到 O(n) 级别。
区间筛法:在大范围中筛选 [L, R] 区间的质数。
