Miller_Rabin 判斷素數

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的素性.d1,n肯定不是素數;d=1,n則很可能是素數,但也存在合數n,使得 .例如,滿足此條件的最小合數是n=341.爲了提高測試的準確性,我們可以隨機地選取整數1<a<n-1,然後用條件 來判定整數n的素性.例如對於n=341,a=3, ,故可判定n不是素數.

       費馬小定理畢竟只是素數判定的一個必要條件.滿足費馬小定理條件的整數n未必全是素數.有些合數也滿足費馬小定理的條件.這些合數被稱作Carmichael,3Carmichael數是561,1105,1729. Carmichael數是非常少的.1~100000000範圍內的整數中,只有255Carmichael.

       利用下面的二次探測定理可以對上面的素數判定算法作進一步改進,以避免將Carmichael數當作素數.

在介紹二次探測定理之前,先介紹一下n的大數冪乘的快速算法.

 

n的大數冪乘的快速算法:

數論計算中經常出現的一種運算就是求一個數的冪a^b對另外一個數n的模的運算,即計算

        a^b mod n (a,bn都是正整數)

      

由於計算機只能表示有限位的整數,所以編程時模取冪的運算要注意值的大小範圍.

如何解決這個問題,我們引出一個能計算 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;

}

發佈了62 篇原創文章 · 獲贊 6 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章