- 适用场景:求2~N范围内的素数
- 优点:线性筛,复杂度为O(n)。与埃氏筛相比,不会对已经被标记过的合数再进行重复标记,故效率更高。欧拉筛将合数分解为 (最小质因数 * 一个自然数) 的形式,通过最小质因数来判断当前合数是否已经被标记过。
- 筛数原理:我们知道当一个数为素数的时候,它的倍数肯定不是素数。所以我们可以从2开始通过乘积筛掉所有的合数。
- 算数基本定理:一个合数可以唯一分解为多个素数的乘积。
- 例如:i循环到77,这是要筛掉77的倍数,2*77,3*77,4*77…这些都为合数,那么筛到什么时候为止呢?
- 77=7*11
- 筛掉2*77=2*7*11
- 筛掉3*77=3*7*11
- 筛掉5*77=5*7*11
- 筛掉7*77=7*7*11
- 停止筛数
- 此时如果继续筛下去,就要筛掉11*77=11*7*11=847,那么这个数也会被121筛,因为7*121=7*11*11=847,产生重复筛出
- 避免重复筛除原理:把一个合数分解为,一个质因子乘以另一个自然数X,欧拉筛就是保证所有合数,只被这个X筛。这样就保证了这个数不会被重复筛出。
- 847=7*11*11,保证847只被11*11,即121在它的回合内筛除(筛2*121,3*121,5*121…)就行了
- 实现方法就是,每个数i只筛到最小质因子乘以自身就停止。
- 代码
class Solution {
public:
int countPrimes(int n) {
vector<int>prime(n);
int num=0;
vector<bool>digit(n,true);
for(int i=2;i<n;i++){
if(digit[i]){
prime[num++]=i;
}
for(int j=0;j<num;j++){
if(i*prime[j]>=n)
break;
digit[i*prime[j]]=false;
if(i%prime[j]==0)
break;
}
}
return num;
}
};
- 算法思想:保证构建合数的路径唯一
- 方法:用一个基数向后筛数的时候,将基数分解为从小到大的质因数相乘
- 如果是素数,则为X1
- 如果是合数,则质因数分解为X1 * X2 * …* Xn
- 规定一种构建合数的方法:即在上面的序列左侧再乘上一个数
- 使用基数向后筛数的过程,即找基数的倍数的过程,即在质因数相乘的序列左侧再乘以一个数的过程,等同在构建合数
- 那么每次构建合数的时候,只允许在左侧乘以一个小于等于X1的素数,那么就保证了构建合数的路径唯一,也就意味着一个合数只会被一个特定的基数筛出,也就不会出现重复筛数的情况。