數學技巧之素數篩選


如果只是對一個整數進行素性測試的只要o(√n)的復雜度便可以判定,藍而如果是n個呢(n<=1000)照樣可以,那如果100000個呢?對於普通的o(n√n)根本跑不動,因此我們必須尋找更加高效的算法,常用的篩選方法有埃氏篩法, 區間篩法,歐拉篩法。

1.埃氏篩法

首先,我們先把2-n范圍內的數寫下來,其中最小的素數是2,那么能被2整除的數便不是素數,那么我們可以把2的倍數都划去。然后剩下的最小素數便是3,我們便把3的倍數都划去,以此類推。這樣反復操作我們就能枚舉n以內的素數。

貼上偽代碼:

1 const int N = 1000000 + 5;
2 int check[N], prime[N];
3 
4 int ptot = 0;
5 memset(check, 0, sizeof(check));
6 for(int i = 2; i <= n; i ++){
7     if(!check[i])prime[ptot++] = i;
8     for(int j = 2; i * j <= n; j ++) check[i * j] = 1;
9 }

 

但是問題又來了, 這個復雜度究竟該怎么算呢?怎么看像o(n2)呢?No No No~~

每次我們篩去每個數的整數倍像2篩了n/2個,3篩了n/3個;

因此總的復雜度約為:

哎呀!這不是發散級數嗎?這個不是發散的數啊,那復雜度不是會爆掉嗎?

別擔心,雖然它是發散級數,但是他的增長速度非常慢,來我們測試一下

看到了嗎?這個發散級數在n = 1000000的時候都沒有超過14由此可見它的復雜度很小,近似等效成o(nloglogn),而對我們這些搞ACM的人來講他大致看成線性的都無妨。

2.區間篩法

設區間[a,b)表示a<=x<b,的素數所構成的集合。

我們很容易知道b只要不能整除[2,√b]里面的素數,那么b就可以被認定為一個素數,因此,如果我們有√b里面的素數表,便可以結合埃氏篩法來判斷。

附上偽代碼:

 1 const int N = 100000 + 5;
 2 int check[N], prime[N];
 3 int is_prime[N];
 4 
 5 int ptot = 0;
 6 memset(check, 0, sizeof(check));
 7 memset(is_prime, 0, sizeof(is_prime));
 8 for(int i = 2; i <= n; i ++){
 9     if(!check[i])prime[ptot++] = i;
10     for(int j = 2; i * j <= sqrt(b); j ++) check[i * j] = 1;//篩[2,√b)
11     for(int j = a/i+1; i * j < b; j ++) is_prime[i * j - a] = 0;//篩[a,b)
12     //將[a, b)的數,移到[0, b - a)可節省空間
13 }

 

3.歐拉篩法

這便是重頭戲,真真正正的o(n)的復雜度,不說什么,直接上代碼。

 1 const int N = 1000000 + 5;
 2 int prime[N], check[N];
 3 
 4 memset(prime, 0, sizeof(prime));
 5 memset(check, 0, sizeof(check));
 6 int ptot = 0;
 7 for(int i = 2; i <= n; i ++){
 8     if(!check[i]) prime[ptot ++] = i;
 9     for(int j = 0; j < ptot; j ++){
10         if(prime[j] * i > n) break;
11         check[prime[j] * i] = 1;
12         if(i % prime[j] == 0) break;
13     }
14 }

 

這個貌似只是做了少許改進了吧,貌似就改進了第12行啊,聰明,藍而就是因為這一行,導致了他的復雜度變成完美的線性, 設合數的最小質因數為p,則當它遍歷到這個素數的時候因為第12行二跳出循環,這樣直接導致了每個合數只能被它的最小質因數給刪去。

下面給出證明:

設合數n的最小質因數為p,另一個比它大的質因數為p1,設 n = p1 * m1 = p * m,我們很容易就可以發現j循環到質因數p的時候合數n第一次被標記(若循環到p之前已經跳出循環則說明有比p更小的質因數),若也被p1標記,則是在這之前(因為p1 > p,所以m1 < m),考慮i循環到m1,注意到n = p*m=p1*m1且p,p1為不同的質數,因此p|m1,所以當j循環到質數p后就結束,不會循環到p1.這就說明了不會被p1篩去,說明合數只能被它最小的素數給刪去。(很神奇吧!)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM