http://blog.csdn.net/fisher_jiang/article/details/986654
關於素數的研究已有相當長的歷史,近代密碼學的研究又給它注入了新的活力.在關於素數的研究中素數的測試是一個非常重要的問題.Wilson 定理給出了一個數是素數的重要條件.
Wilson 定理 對於給定的正整數 n,判定 n 是一個素數的充要條件是
(n-1)!≡ -1(mod n)
Wilson 定理有很高的理論價值.但實際用於素數測試所需要計算量太大,無法實現對較大素數的測試.到目前爲止,尚未找到素數測試的有效的確定性算法.
首先容易想到下面的素數測試概率算法Prime
bool Prime(unsigned int n)
{
//rnd.Random(n)返回0~n-1之間的隨機整數
RandomNumber rnd;
int m=floor(sqrt(double(n)));
unsigned int a=rnd.Random(m-2)+2;
return (n%a!=0);
}
算法Prime返回false時,算法幸運地找到n的一個非平凡因子,因此可以肯定n是一個合數.但是對於上述算法Prime來說,即使n是一個合數,算法仍可以高概率返回true.例如,當n=2653=43*61時,算法在2~51範圍內隨機選擇一個整數a,僅當選擇到a=43時,算法返回false.其餘情況均返回true.在2~51範圍內選到a=43的概率約爲2%,因此算法以98%的概率返回錯誤的結果true.當n增大時,情況就更糟.
著名的費馬小定理爲素數判定提供一個有力的工具
費馬小定理: 如果p是一個素數,且(0<a<p),則
例如,67是一個素數,則2^66 mod 67=1.
利用費馬小定理,對於給定的整數n,可以設計一個素數判定算法.通過計算d=2^(n-1) mod n 來判定整數n的素性.當d≠1時,n肯定不是素數;當d=1時,n則很可能是素數,但也存在合數n,使得 .例如,滿足此條件的最小合數是n=341.爲了提高測試的準確性,我們可以隨機地選取整數1<a<n-1,然後用條件 來判定整數n的素性.例如對於n=341,取a=3時,有 ,故可判定n不是素數.
費馬小定理畢竟只是素數判定的一個必要條件.滿足費馬小定理條件的整數n未必全是素數.有些合數也滿足費馬小定理的條件.這些合數被稱作Carmichael數,前3個Carmichael數是561,1105,1729. Carmichael數是非常少的.在1~100000000範圍內的整數中,只有255個Carmichael數.
利用下面的二次探測定理可以對上面的素數判定算法作進一步改進,以避免將Carmichael數當作素數.
在介紹二次探測定理之前,先介紹一下模n的大數冪乘的快速算法.
模n的大數冪乘的快速算法:
數論計算中經常出現的一種運算就是求一個數的冪a^b對另外一個數n的模的運算,即計算
a^b mod n (a,b和n都是正整數)
由於計算機只能表示有限位的整數,所以編程時模取冪的運算要注意值的大小範圍.
如何解決這個問題,我們引出一個能計算 a^b mod n 的值的有用算法-----反覆平方法.
首先我們必須明確:
由此引出一個迭代式
d=a;
for( i=2;i<=b;i++)
{
d=| d mod n| *a;
d=d mod n
}
問題是當b很大時,運行的時間將受之影響,爲了提高時效,我們不妨將b轉換爲二進制數:
然後從最低位b0開始,由右至左逐位掃描.每次迭代時,用到下面兩個恆等式中的一個:
bi=0
bi=1 (0<=c<=b)
其中c爲 b的二進制數的後綴 (bi-1,bi-2,….b0)對應的十進制數,當c成倍增加時,算法保持條件 d=a^c mod n不變,直至 c=b.
下面爲程序分析,函數modular_exp(long a,long b,long n)輸入底數a,次冪b和模n後,通過反覆平方法計算和返回a^bmod n的值.
long modular_exp(long a,long b,long n)//d≡a^b mod n
{
long d=1;
long t=a;
while(b>0)
{
if(b%2==1)
d=(d*t)%n;
b=b/2;
t=(t*t)%n;
}
return d;
}
二次探測定理 如果p是一個素數,且0<x<p,則方程x*x≡1(mod p)的解爲x=1,p-1.
事實上, x*x≡1(mod p)等價於 x*x-1≡0(mod p).由此可知;
(x-1)(x+1) ≡1(mod p)
故p必須整除x-1或x+1.由p是素數且 0<x<p,推出x=1或x=p-1.
利用二次探測定理,我們可以在利用費馬小定理計算 a^(n-1) mod n的過程中增加對於整數n的二次探測.一旦發現違背二次探測條件,即可得出n不是素數的結論.
下面的算法power用於計算a^p mod n,並在計算過程中實施對n的二次探測.
void power(unsigned long a,unsigned long p,unsigned long n,unsigned long &result,bool &composite)
//計算 a^p mod n,並實施對n的二次探測
{
unsigned long x;
if(p==0) result=1;
else
{
power(a,p/2,n,x,composite);//遞歸計算
result=(x*x)%n;//二次探測
if((result==1)&&(x!=1)&&(x!=n-1))
composite=true;
if((p%2)==1) //p是奇數
result=(result*a)%n;
}
}
在算法power的基礎上,可設計Miller_Rabin素數測試的算法如下:
bool Miller_Rabin(unsigned long n)
{
RandomNumber rnd;
unsigned long a,result;
bool composite=false;
a=rnd.Random(n-3)+2;
power(a,n-1,n,result,composite);
if(composite||(result!=1)) return false;
else return true;
}
上述算法返回false時,整數n一定是一個合數,而當返回值爲true時,整數n在高概率意義下是一個素數.仍然可能存在合數n,對於隨機選取的基數a,算法返回true.但對於上述算法的深入分析表明,當n充分大時,這樣的基數a不超過(n-9)/4個. Miller_Rabin算法的錯誤概率可通過多次重複調用而迅速降低.重複調用k次的Miller_Rabin算法可描述如下:
bool Miller_Rabin(unsigned long n,unsigned int k)
//重複k次調用
{
RandomNumber rnd;
unsigned long a,result;
bool composite=false;
for(int i=1;i<=k;i++)
{
a=rnd.Random(n-3)+2;
power(a,n-1,n,result,composite);
if(composite||(result!=1)) return false;
}
return true;
}
分析得上述算法的錯誤概率不超過,這是一個很保守的估計,實際使用的效果要好得多.
附:上述程序中的類RandomNumber定義如下:
class RandomNumber
{
private ://當前種子
unsigned long randSeed;
public:
//構造函數,缺省值0表示由系統自動產生種子
RandomNumber( unsigned long s=0)
//產生0~n-1之間的隨機整數
unsigned long Random( unsigned long n)
}
RandomNumber::RandomNumber(unsigned long s)
{
if(s==0)
{
randSeed= (unsigned long)time(0);
}
else
{
randSeed=s;
}
}
unsigned long RandomNumber:Random(unsigned long n)
{
randSeed=multiplier * randSeed + adder;
return randSeed % n;
}