1. 程式人生 > >關於埃式篩法的極限優化

關於埃式篩法的極限優化

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