1. 欧拉筛法概述
欧拉筛法(Euler's Sieve)是一种高效的素数筛选算法,能够在O(n)的时间复杂度内找出小于等于n的所有素数。与传统的埃拉托斯特尼筛法(时间复杂度O(n log log n))相比,欧拉筛法通过确保每个合数只被其最小质因数筛除一次,实现了线性时间复杂度。
我第一次接触欧拉筛法是在解决一个需要快速生成大量素数的问题时。当时使用埃氏筛法遇到性能瓶颈,转而研究更高效的筛法,欧拉筛法的精妙设计让我印象深刻——它通过维护一个素数列表,并利用每个合数的最小质因数特性,避免了重复标记。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 算法原理与核心思想
2.1 基本工作原理
欧拉筛法的核心在于:每个合数n只会被它的最小质因数p筛除一次。算法维护一个素数列表pri[],对于每个整数i(从2到n):
- 如果i未被标记为合数,则i是素数,加入pri列表
- 遍历当前pri列表中的每个素数p:
a. 标记i×p为合数
b. 如果i能被p整除,则停止当前循环
cpp复制vector<int> pri;
bool not_prime[N];
void euler_sieve(int n) {
for(int i=2; i<=n; i++) {
if(!not_prime[i]) pri.push_back(i);
for(int p : pri) {
if(i*p > n) break;
not_prime[i*p] = true;
if(i%p == 0) break;
}
}
}
2.2 关键优化点
- 最小质因数筛选:当i%p==0时break,确保每个合数只被最小质因数筛除
- 提前终止:i*p>n时终止内层循环,减少不必要的计算
- 空间优化:使用位压缩技术可进一步减少内存占用
注意:
i%p==0时的break是算法关键,它保证了时间复杂度为线性。此时i包含p作为因子,任何更大的p'×i都会被p'的最小质因数在后续处理中筛除。
3. 算法实现细节
3.1 完整C++实现
cpp复制#include <vector>
using namespace std;
const int N = 1e8 + 10; // 筛法上限
vector<int> primes; // 存储素数
bool is_composite[N]; // 标记数组
void linear_sieve(int n) {
for(int i=2; i<=n; ++i) {
if(!is_composite[i])
primes.push_back(i);
for(int p : primes) {
if(i*p > n) break;
is_composite[i*p] = true;
if(i%p == 0) break; // 关键优化
}
}
}
3.2 时间复杂度分析
- 外层循环执行n-1次(2到n)
- 内层循环中,每个合数只被标记一次
- 总操作次数约为n + Σ(p≤n) n/p ≈ O(n)
数学证明:每个合数n只会被其最小质因数p标记,且标记操作是O(1)的,因此总时间复杂度为O(n)。
4. 应用场景与扩展
4.1 常见应用
- 素数计数问题:快速统计区间内素数数量
- 质因数分解预处理:可同时记录每个数
