埃氏筛
原理:一个数如果不是小于等于它的所有质数的倍数,那么这个数就是质数,质数的倍数一定都是合数根据以上原理,可以把题目已给范围内所有质数筛选出来,只需遍历所有数,把质数的倍数去除(标记为false),并且从前往后遍历的过程中每对一个质数进行以上操作,下一个为没被筛出的数一定也为质数,因为下一个数前面的所有质数的倍数都被去除了,这个数仍然留着说明这个数不是小于等于它的所有质数的倍数,因此一定是质数,举例:
初始时2为质数,把所有2的倍数去除后,遍历到下一个没有去除的3,3一定是质数,因为它不是小于它的所有的质数(也就是2)的倍数
接着把3的倍数去除,下一个没去除的5同样一定是质数,就这样慢慢的把范围内所有合数去除最终筛选出质数
问题:
根据上图可知考虑不同的质数时会去除重复的数(上图打×的数),虽然不影响结果,但会增加复杂度
优化:
1.遍历每个质数时可以从质数的平方开始去除,例如i=3时,可以从j=9开始去除,因为小于 i*i 的合数 j 一定有一个小于 i 的质因子。可以用反证法证明:(合数一定能拆分成几个质数的乘积)如果小于 i*i 的合数 j 没有小于 i 的质因子,那么合数 j 至少是两个 ≥i 的数的乘积,这与j<i*i矛盾.
2.有的题目遍历i的时候如果写成以下形式可能会出现下标越界:
for(int j=i*i;j<MX+1;j+=i){
is_primes[j]=false; //质数的倍数都标记为合数
}
//遍历时可能会有i*i>MX的情况,因此可以让j遍历i的倍数
3.进一步优化-欧拉筛(线性筛)
代码:
int MX; //根据数据范围确定
vector<bool> is_primes(MX+1,true); //[0,MX]区间内所有质数为true
//C++11 lambda表达式,初始化is_primes
auto init=[]{
is_primes[1]=false; //1为合数
for(int i=2;i<MX+1,i++){ //遍历[2,MX]所有数,排除合数
if(is_primes[i]){ //若当前的i是质数
for(int j=i;j<MX/i+1;j++){ //遍历i的倍数,可以防止越界
is_primes[i*j]=false; //质数的倍数都标记为合数
}
}
}
return 0;
}();
欧拉筛-每个合数只被划掉一次
原理:每个数乘以小于等于它本身的所有质数得到的合数被划掉,就可以划掉所有合数,但是会有重复划掉的情况,进一步优化,划掉每个数乘以小于等于它本身的最小质因子的质数得到的合数,不会有重复去除的情况,例如:
过程:遍历2把2加入primes,去除2*2=4,遍历3把3加入primes,去除3*2=6,3*3=9,遍历4,4不是质数不加入primes,4的最小质因子是2,因此只需要去除primes中<=2的质数和它相乘得到的合数4*2=8,因为4的最小质因子是2,它乘以任何数得到的数最小质因子也一定是2,质因子是2的数会在之后被清除掉,所以不用再重复考虑(考虑6的时候6*2=12),这就是不重复去除的原理
int MX; //根据数据范围确定
vector<bool> is_primes(MX+1,true); //[0,MX]区间内所有质数为true
vector<int> primes; //加入质数
//C++11 lambda表达式,初始化is_primes
auto init=[]{
is_primes[1]=false; //1为合数
for(int i=2;i<Mx+1,i++){ //遍历[2,MX]所有数,排除合数
if(is_primes[i]) //若当前的i是质数
primes.emplace_back(i);
//每个数i只乘以小于等于它的最小质因子(lpf[i])的所有质数,就不会重复去除
for(int j=0;j<primes.size();j++){ //遍历之前所有的质数
if(primes[j]*i>MX) //越界
break;
is_primes[i*primes[j]]=false;
if(i%primes[j]==0) //碰到的第一个除的尽的质数就是最小质因子,碰到最小质因子直接退出
break;
}
}
return 0;
}();
选择:
在题目所数的范围比较小的时候可以使用埃氏筛,因为欧拉筛中有取模运算,反而会让运算时间更长,但当数的范围特别大的时候,去除的重复合数个数变多,埃氏筛就会比欧拉筛用时长
[2614. 对角线上的质数](https://leetcode.cn/problems/prime-in-diagonal/) 1375
[3115. 质数的最大距离](https://leetcode.cn/problems/maximum-prime-difference/) 1294
[2523. 范围内最接近的两个质数](https://leetcode.cn/problems/closest-prime-numbers-in-range/) 1650
[762. 二进制表示中质数个计算置位](https://leetcode.cn/problems/prime-number-of-set-bits-in-binary-representation/) 1383