摘自https://blog.csdn.net/qq_43332305/article/details/82959066
关于素数的普通筛法想必大家都清楚。使用一个数组vis[n],从2遍历到n-1,每次碰到素数就把它的倍数剔除。这里有三种手段可以大大降低埃式筛法的时间复杂度:
先发埃式筛法模板,假设初始化vis所有成员为0,0代表是素数,1代表不是素数:
1 for(int i=2;i<=N;i++){ 2 if(!vis[i]){ 3 for(int j=2*i;j<=N;j+=i){ 4 vis[j]=1; 5 } 6 } 7 }
那么首先会想到一个问题,一个合数如果有因数,那么必定是成对存在,也就是筛子只用判断其中一个因数就可以了,比如15=3*5,在3的时候已经把15给筛掉了,何必去循环到5呢?于是优化如下,循环到根号n即可。
1 for(int i=2;i*i<=N;i++){//将i<=N换成i*i<=N 2 if(!vis[i]){ 3 for(int j=2*i;j<=N;j+=i){ 4 vis[j]=1; 5 } 6 } 7 }
接下来是第二重优化。第二层循环中不是从j=2i开始,每次增加一个i,即剔除i的所有倍数吗?试想一下,6是不是被2筛过了,此时碰到素数3又要筛一遍。而j从2i到i*i,即2倍到i倍的变化,在第一层循环中,2~i-1已经把他们的素数倍数筛完,所以结论如下:
第i个数只用筛从 i 乘 i 开始到n的每个数,而不用从 2 乘 i 开始
接下来优化代码:
1 for(int i=2;i*i<=N;i++){//将i<=N换成i*i<=N 2 if(!vis[i]){ 3 for(int j=i*i;j<=N;j+=i){//将2*i换成i*i 4 vis[j]=1; 5 } 6 } 7 }
第三重优化是一个很小的改动与优化,在第二重循环中,每次增加素数的1倍,但除了2以外素数都是奇数,那么奇数乘以偶数是偶数,偶数情况早已被2筛完,故每次增加2i倍.
1 for(int i=2;i*i<=N;i++){//将i<=N换成i*i<=N 2 if(!vis[i]){ 3 int mul;//倍数 4 i==2?mul=1:mul=2; 5 for(int j=i*i;j<=N;j=j+i*mul){//将2*i换成i*i 6 vis[j]=1; 7 } 8 } 9 }