1. 程式人生 > 實用技巧 >暴力判斷素數、埃氏篩法、尤拉篩法

暴力判斷素數、埃氏篩法、尤拉篩法

篩法是一種用來求素數的方法。一般來說可以循序漸進的掌握暴力判斷素數、埃氏篩法、尤拉篩法。

暴力判斷素數

時間複雜度 O(n²/2)(對於打出素數表來說)(我太蒻了,這個時間複雜度不保證正確)

利用素數只能被1和它本身整除的性質,我們可以把數x與2到n-1全取餘。如果結果均不等於0,則為素數。

程式碼

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstdlib>
 4 
 5 using namespace std;
 6 
 7 int n;
 8 bool flag;
 9 
10 int main()
11 { 12 scanf("%d",&n); 13 14 for(int i=2;i<=n;i++) 15 { 16 flag=true; 17 for(int j=2;j<=i-1;j++) 18 { 19 if(i%j==0) 20 { 21 flag=false; 22 //當出現第一個能被整除的數時立即跳出迴圈,可提高一定效率 23 break; 24
} 25 } 26 27 if(flag) printf("%d ",i); 28 } 29 30 return 0; 31 }

這個最簡單的寫法在只判斷幾個素數時綽綽有餘,但是當要打出一個很大的素數表時就很狼狽了。

埃氏篩法

時間複雜度 O(nloglogn)

很容易想到,任意一個素數的倍數是合數,這就是埃氏篩法篩出素數表的原理。假設要打出n以內的素數表。我們從2開始向後遍歷,每當遇到一個素數p時,便利用此素數將[2,n]內p*2,p*3,p*4......這些合數全部篩去。

程式碼

 1
#include <iostream> 2 #include <cstdio> 3 #include <cstdlib> 4 #include <cmath> 5 6 using namespace std; 7 8 int n; 9 int p[10000010]; 10 11 int main() 12 { 13 scanf("%d",&n); 14 15 //初始化 16 for(int i=2;i<=n;i++) p[i]=1; 17 18 for(int i=2;i<=sqrt(n)+0.5;i++) 19 for(int j=i*2;j<=n;j+=i) p[j]=0; 20 21 for(int i=2;i<=n;i++) 22 if(p[i]) printf("%d ",i); 23 24 return 0; 25 }

這個演算法在一般的資料面前已經足夠了。不過我們還可以更快:)

尤拉篩法

時間複雜度 O(n)

首先觀察一下埃氏篩法。在使用他時,我們發現有這樣一個問題:在使用素數2進行篩去操作時,我們會篩去素數6;在之後利用素數3進行篩去操作時,我們又會篩去一次素數6。可見在埃氏篩法的篩去過程中,會出現同一個合數篩去多次的情況,以至於影響了效率。

相比之下,尤拉篩法是一種線性篩法。它便解決了埃氏篩法中一個合數會被篩去多次的問題,確保了每一個合數都只會被篩去一次,因此具有O(n)的效率。而它的實現原理是確保每個合數都只會被它的最小質因數篩掉。過程大概如下:

首先我們會建立2個數組,isp(值為1/0,isp[i]用來表示i是否是素數),p(用來儲存所有的素數)。依舊是打出到n的素數表。

1.將i從2開始遍歷。如果i是素數,就把它加入到p[]中。

2.在當前i的情況下遍歷p[],篩去i*p[j],若i*p[j]>n(已經沒必要篩去了)或i%p[j]==0(這是確保每個合數只篩一次的核心)就退出迴圈。

對核心的解釋:假設在i%p[j]==0時我們繼續進行,便篩去了合數i*p[j+1]。由於此時i%p[j]==0,則這個由一個素數和一個合數的乘積組成的合數,一定能轉化為一個更小的素數和一個更大的合數的乘積,那麼當繼續執行時,此合數必然還會被p[j]再次篩去,便進行了多餘的篩去工作。因此可以說在i%p[j]==0時退出迴圈是確保每個合數都只會被它的最小質因數篩掉,也就是確保每個合數只被篩一次的核心。

程式碼

 1 #include <iostream>
 2 #include <cstdio>
 3 #include <cstdlib>
 4 
 5 using namespace std;
 6 
 7 int n;
 8 int num=0;
 9 int isp[10000010],p[10000010];
10 
11 int main()
12 {
13     scanf("%d",&n);
14     
15     //初始化
16     for(int i=2;i<=n;i++) isp[i]=1;
17     
18     for(int i=2;i<=n;i++)
19     {
20         if(isp[i])
21         {
22             num++;
23             p[num]=i;
24         }
25         for(int j=1;j<=num&&i*p[j]<=n;j++)
26         {
27             isp[i*p[j]]=0;
28             //確保每個合數都只會被它的最小質因數篩掉
29             if(i%p[j]==0) break;
30         }
31     }
32     
33     for(int i=1;i<=num;i++) printf("%d ",p[i]);
34     
35     return 0;
36 }