关于埃式筛法的极限优化

摘自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     }

 

posted @ 2018-12-13 22:05  codeoos  阅读(870)  评论(0编辑  收藏  举报