素數篩法


  素數的篩法有很多種,但是基礎就是對素數的判定。即,我們需要知道什么是素數,以及素數的一些性質,那么我們先講一講素數的性質(這一部分一定要好好掌握,對考試有很大的幫助):

  定義:只有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):素數有無窮多個

    下面來證明這個結論:

    用反證法,假設自然數中只有有限多個素數,可以記為P,P,P……Pk ,這時存在一個正整數N = P1P2P……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 * 2則要么ad ≡ 1(mod n),要么存在0 < i < r ,s.t.ad*2i ≡ -1 ( mod n )

  知道了原理,就可以帶進幾個數進行計算,得出是否為素數(代碼中的gg數組中的幾個質數在int范圍內不會判斷出錯),至於代碼,也比較好理解,就不再說了。

 

這就是本篇博文的所有內容,請大佬們多多指正,不喜勿噴QwQ

2019-04-09 21:31:23


免責聲明!

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



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