Miller_Rabin素數測試
關於素數的研究已有相當長的歷史,近代密碼學的研究又給它注入了新的活力.在關於素數的研究中素數的測試是一個非常重要的問題.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不是素數.
費馬小定理畢竟只是素數判定的一個必要條件
利用下面的二次探測定理可以對上面的素數判定演算法作進一步改進,以避免將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;
}