轉載自CSDN博客:https://blog.csdn.net/huang_miao_xin/article/details/51331710
定義:約數只有1和本身的整數稱為質數,或稱素數。
計算機或者相關專業,基本上大一新生開始學編程都會接觸的一個問題就是判斷質數,下面分享幾個判斷方法,從普通到高效。
1)直觀判斷法
最直觀的方法,根據定義,因為質數除了1和本身之外沒有其他約數,所以判斷n是否為質數,根據定義直接判斷從2到n-1是否存在n的約數即可。C++代碼如下:
bool isPrime_1( int num )
{
int tmp =num- 1;
for(int i= 2;i <=tmp; i++)
if(num %i== 0)
return 0 ;
return 1 ;
}
2)直觀判斷法改進
上述判斷方法,明顯存在效率極低的問題。對於每個數n,其實並不需要從2判斷到n-1,我們知道,一個數若可以進行因數分解,那么分解時得到的兩個數一定是一個小於等於sqrt(n),一個大於等於sqrt(n),據此,上述代碼中並不需要遍歷到n-1,遍歷到sqrt(n)即可,因為若sqrt(n)左側找不到約數,那么右側也一定找不到約數。
C++代碼如下:
C++代碼如下:
-
bool isPrime_2( int num )
-
{
-
int tmp =
sqrt( num);
-
for(
int i=
2;i <=tmp; i++)
-
if(num %i==
0)
-
return
0 ;
-
return
1 ;
-
}
3)另一種方法
方法(2)應該是最常見的判斷算法了,時間復雜度O(sqrt(n)),速度上比方法(1)的O(n)快得多。最近在網上偶然看到另一種更高效的方法,暫且稱為方法(3)吧,由於找不到原始的出處,這里就不貼出鏈接了,如果有原創者看到,煩請聯系我,必定補上版權引用。下面講一下這種更快速的判斷方法;
首先看一個關於質數分布的規律:大於等於5的質數一定和6的倍數相鄰。例如5和7,11和13,17和19等等;
證明:令x≥1,將大於等於5的自然數表示如下:······ 6x-1,6x,6x+1,6x+2,6x+3,6x+4,6x+5,6(x+1),6(x+1)+1 ······
可以看到,不在6的倍數兩側,即6x兩側的數為6x+2,6x+3,6x+4,由於2(3x+1),3(2x+1),2(3x+2),所以它們一定不是素數,再除去6x本身,顯然,素數要出現只可能出現在6x的相鄰兩側。這里有個題外話,關於孿生素數,有興趣的道友可以再另行了解一下,由於與我們主題無關,暫且跳過。**這里要注意的一點是,在6的倍數相鄰兩側並不是一定就是質數**。
此時判斷質數可以6個為單元快進,即將方法(2)循環中i++步長加大為6,加快判斷速度,原因是,假如要判定的數為n,則n必定是6x-1或6x+1的形式,對於循環中6i-1,6i,6i+1,6i+2,6i+3,6i+4,其中如果n能被
6i,6i+2,6i+4整除,則n至少得是一個偶數,但是6x-1或6x+1的形式明顯是一個奇數,故不成立;另外,如果n能被6i+3整除,則n至少能被3整除,但是6x能被3整除,故6x-1或6x+1(即n)不可能被3整除,故不成立。綜上,循環中只需要考慮6i-1和6i+1的情況,即循環的步長可以定為6,每次判斷循環變量k和k+2的情況即可,理論上講整體速度應該會是方法(2)的3倍。 代碼如下: ``` bool isPrime_3( int num ) { //兩個較小數另外處理 if(num ==2|| num==3 ) return 1 ; //不在6的倍數兩側的一定不是質數 if(num %6!= 1&&num %6!= 5) return 0 ; int tmp =sqrt( num); //在6的倍數兩側的也可能不是質數 for(int i= 5;i <=tmp; i+=6 ) if(num %i== 0||num %(i+ 2)==0 ) return 0 ; //排除所有,剩余的是質數 return 1 ; } ``` > 這邊為什么是5開始取?為什么不是7?這邊還有個邏輯上的取舍 從5、7開始的區別在於i <= sqrt(num)..如果是5的話,判斷條件為25;如果是7的話,判斷的條件就為49。 而仔細觀察49內的所有質數,發現25之前的質數都是6k左右的數(6k-1,6k+1),而25以后,就不定都有了。如26則不為質數。 所以如果從5開始的話,那么25以內的數 都不會進入for循環,經過`if(num %6!= 1&&num %6!= 5)`的篩選后,就都是素數了。 而如果是從7開始,那么25-49之內的數不符合條件卻不會進入for循環,所以26缺少這個for的循環判斷后就被誤判為素數了。 ==>以我淺薄的數學見識理解,25以內素數規律的巧合性使得 這些數不需要進入for循環判斷,所以相比於從7開始的錯誤,5開始是正確的
算法性能測試:
編寫測試代碼,使用較多數據測試比較幾種方法的判斷效率,數據量40w,代碼如下:
#include <iostream>
#include <string>
#include <ctime>
#include <vector>
using namespace std;
bool isPrime_1( int num );
bool isPrime_2( int num );
bool isPrime_3( int num );
int main()
{
int test_num =400000;
int tstart ,tstop; //分別記錄起始和結束時間
//測試第一個判斷質數函數
tstart=clock ();
for(int i= 1;i <=test_num; i++)
isPrime_1(i );
tstop=clock ();
cout<<"方法(1)時間(ms):" <<tstop- tstart<<endl ;//ms為單位
//測試第二個判斷質數函數
tstart=clock ();
for(int i= 1;i <=test_num; i++)
isPrime_2(i );
tstop=clock ();
cout<<"方法(2)時間(ms):" <<tstop- tstart<<endl ;
//測試第三個判斷質數函數
tstart=clock ();
for(int i= 1;i <=test_num; i++)
isPrime_3(i );
tstop=clock ();
cout<<"方法(3)時間(ms):" <<tstop- tstart<<endl ;
cout<<endl ;
system("pause" );
return 0 ;
}
運行結果如下;
可以看出,判斷到40w,效率上方法(1)明顯要差得多,方法(2)和方法(3)在這種測試數量下時間相差2倍多
單獨對比方法(2)和(3),數據量加到1000w,結果如下:
可以看出,方法(2)和方法(3)在這種測試數量下時間相差依然是2倍多,不過已經是很不錯的提升。
對了,附上運行環境,CPU-i5-3210,內存4G,win7,vs2012。
