素數的篩法有很多種,但是基礎就是對素數的判定。即,我們需要知道什么是素數,以及素數的一些性質,那么我們先講一講素數的性質(這一部分一定要好好掌握,對考試有很大的幫助):
定義:只有1和自身作為因子(就是因數,不用我再贅述了)的數叫做素數(也叫質數)。
性質(1):以π(x)表示不超過x的素數個數,可以證明----lim π(x) ln x ÷ x = 1(lim表示x趨近於正無窮)
根據這一性質,我們可以推得一個式子,它長這樣:
lim π(x) = x / ln x,
雖然我暫時還沒有用到這個式子,但是聽鍾神說可以利用這個式子估計算法的復雜度,就先記下來了。
性質(2):設a是任意大於1的整數,則a的除1以外的最小正約數q必是素數;當a是合數時,q ≤ sqrt( a )
我們來證明一下:
我們可以用反證法,假設q不是素數,由q不為一可得q一定是合數,此時,存在q1為q的因子,且滿足1 < q1 < q,q1 | q;又因為q |a,這與q是a的除1以外的最小正約數矛盾;
當a是合數時,設a = a1q,其中q為a的除1以外的最小正約數,則a1 ≥ q,q2 ≤ a,即q ≤ sqrt ( a )
性質(3):素數有無窮多個
下面來證明這個結論:
用反證法,假設自然數中只有有限多個素數,可以記為P1 ,P2 ,P3 ……Pk ,這時存在一個正整數N = P1P2P3 ……Pk +1,這時由性質(2)可得一定有一個素數P,使得P | N,但是P一定不是已知的素數(否則P | 1),我們就找到了另一個素數,與假設不相符,得證
講完了這幾個性質,我們便可以進行對素數的判別了(這一部分我將結合代碼實現講解)
篩法一:暴力篩
1 #include<cstdio> 2 #include<iostream> 3 #include<cstdlib> 4 #define maxn 10000010 5 using namespace std; 6 bool zi_ran_shu[maxn]; 7 bool shai_su_shu(int); //如果這個數是質數,就返回1,否則,就返回0; 8 int main() 9 { 10 int m; 11 scanf("%d",&m); 12 for(int i=1;i<=m;++i) 13 if(shai_su_shu(i)) zi_ran_shu[i]=1;//對每一個小於m的數進行判定,把判定結果存到zi_ran_shu數組里; 14 for(int i=1;i<=m;++i) 15 if(zi_ran_shu[i]) printf("%d ",i); 16 return 0; 17 } 18 19 bool shai_su_shu(int a) 20 { 21 if(a==1) return 0; 22 if(a==2) return 1; //對1和2的特判 23 for(int i=2;i<=a-1;++i)//要從i=2開始模擬對a做除法,一直枚舉到a-1; 24 if(a%i==0) return 0;//如果出現可以整除a的數,就說明a不是一個素數,return 0; 25 return 1;//如果沒有找到一個數可以整除a,就說明a是一個素數,return 1; 26 }
接下來,思考對這個算法的優化;
如果沒有思路,看看這句話 = = > 結合性質(2),可以想到將篩_素_數函數中的循環改成循環到sqrt(a),這樣就完成了優化,這其實就是誒氏篩法
篩法二:線性篩
1 #include<cstdio> 2 #include<iostream> 3 #include<cstdlib> 4 #include<algorithm> 5 #include<cmath> 6 #include<iomanip> 7 #include<cstring> 8 #include<string> 9 #define maxn 10000010 10 using namespace std; 11 int prime[maxn],tot=0; 12 bool is_prime[maxn]; 13 int main() 14 { 15 memset(is_prime,true,sizeof(is_prime)); 16 int n; 17 scanf("%d",&n); 18 for(int i=2;i<=n;++i) 19 { 20 if(is_prime[i]==true) prime[tot++]=i; 21 for(int j=0;j<tot && i*prime[j]<=n;++j) 22 { 23 is_prime[i*prime[j]]=false;//對所有質數的倍數進行排除(線性篩主要是減少了這一步的重復) 24 if(i%prime[j]==0) break; 25 } 26 } 27 for(int i=0;i<=n;++i) 28 { 29 if(prime[i]) printf("%d ",prime[i]); 30 } 31 return 0;
篩法三:Miller-Rabbin素性測試
1 #include<cstdio> 2 #include<iostream> 3 #include<cstdlib> 4 using namespace std; 5 int gg[8] = {2,3,5,7,13,29,37,89}; 6 7 int kuaisumi(int a,int b,int c){ 8 int ans = 1; 9 int base = a%c; 10 while(b){ 11 if(b & 1) ans = (ans*base)%c; 12 base = (base*base)%c; 13 b >>= 1; 14 } 15 return ans; 16 } 17 18 bool miller_rabin(int a,int n) 19 { 20 int d=n-1,r=0; 21 while (d%2==0) 22 d/=2,r++; 23 int x = kuaisumi(a,d,n); 24 if (x==1) return true; 25 for (int i=0;i<r;i++) 26 { 27 if (x==n-1) return true; 28 x=(long long)x*x%n; 29 } 30 return false; 31 } 32 33 bool is_prime(int n) 34 { 35 if (n<=1) return false; 36 for (int a=0;a<8;a++) 37 if (n==gg[a]) return true; 38 for (int a=0;a<8;a++) 39 if (!miller_rabin(gg[a],n)) return false; 40 return true; 41 } 42 43 int main() 44 { 45 int n; 46 scanf("%d",&n); 47 for(int i=1;i<=n;++i) 48 if(is_prime(i)) printf("%d ",i); 49 return 0; 50 }
先來介紹原理:如果n為素數,取a < n,設n - 1 = d * 2r 則要么ad ≡ 1(mod n),要么存在0 < i < r ,s.t.ad*2i ≡ -1 ( mod n )
知道了原理,就可以帶進幾個數進行計算,得出是否為素數(代碼中的gg數組中的幾個質數在int范圍內不會判斷出錯),至於代碼,也比較好理解,就不再說了。
這就是本篇博文的所有內容,請大佬們多多指正,不喜勿噴QwQ
2019-04-09 21:31:23