miller_rabin_素性測試


 

摘自:http://blog.csdn.net/pi9nc/article/details/27209455

看了好久沒看懂,最后在這篇博客中看明白了。

費馬定理的應用,加上二次探測定理。

 

Fermat素數測試

1819年有人發現了Fermat小定理逆命題的第一個反例:雖然2的340次方除以341余1,但341=11*31。后來,人們又發現了561, 645, 1105等數都表明a=2時Fermat小定理的逆命題不成立。人們把所有能整除2^(n-1)-1的合數n叫做偽素數(pseudoprime)。

不滿足的n一定不是素數;如果滿足的話則多半是素數。這樣,一個比試除法效率更高的素性判斷方法出現了:制作一張偽素數表,記錄某個范圍內的所有偽素數,那么所有滿足且不在偽素數表中的n就是素數。之所以這種方法更快,是因為我們可以使用二分法快速計算的值(快速冪)。

然而不借助偽素數表的時候,算法出錯的概率太高,需要改進.

我們剛才只考慮了a=2的情況。一個合數可能在a=2時通過了測試,但a=3時的計算結果卻排除了素數的可能。於是,人們擴展了偽素數的定義,稱滿足a^(n-1) mod n = 1的合數n叫做以a為底的偽素數(pseudoprime to base a)

隨機選擇若干個小於待測數的正整數作為底數a進行若干次測試,只要有一次沒有通過測試就立即把這個數扔回合數的世界。這就是Fermat素性測試

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

***費馬小定理的前提是a和n互質。當n本身就是素數的時候如果a<n那么a和n始終互素;但n不是素數時a和n不互素的話不能用費馬小定理。也就是說,Carmichael數需要排除a和n不互素的情況.

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

Miller_Rabin素數測試算法

二次探測定理優化

 

 Miller和Rabin兩個人的工作讓Fermat素性測試邁出了革命性的一步,建立了Miller-Rabin素性測試算法。新的測試基於下面的定理:

如果p是素數,x是小於p的正整數,且,那么要么x=1,要么x=p-1。

這是顯然的,因為相當於p能整除,也即p能整除(x+1)(x-1)。

由於p是素數,那么只可能是x-1能被p整除(此時x=1) 或 x+1能被p整除(此時x=p-1)。

我們下面來演示一下上面的定理如何應用在Fermat素性測試上。前面說過341可以通過以2為底的Fermat測試,因為2^340 mod 341=1。如果341真是素數的話,那么2^170mod 341只可能是1或340;當算得2^170 mod 341確實等於1時,我們可以繼續查看2^85除以341的結果。我們發現,2^85 mod 341=32,這一結果摘掉了341頭上的素數皇冠

這就是Miller-Rabin素性測試的方法。不斷地提取指數n-1中的因子2,把n-1表示成(其中d是一個奇數)。那么我們需要計算的東西就變成了除以n的余數。於是,要么等於1,要么等於n-1。如果等於1,定理繼續適用於,這樣不斷開方開下去,直到對於某個i滿足或者最后指數中的2用完了得到的這樣,Fermat小定理加強為如下形式:


盡可能提取因子2,把n-1表示成,如果n是一個素數,那么或者,或者存在某個i使得 ( 0<=i<r ) (注意i可以等於0,這就把的情況統一到后面去了)

 


Miller-Rabin素性測試同樣是不確定算法,我們把可以通過以a為底的Miller-Rabin測試的合數稱作以a為底的強偽素數(strong pseudoprime)。第一個以2為底的強偽素數為2047。第一個以2和3為底的強偽素數則大到1 373 653。
Miller-Rabin算法的代碼也非常簡單:計算d和r的值(可以用位運算加速,即快速積,快速冪),然后二分計算的值,最后把它平方r次。

/*對應hoj 1356 Prime Judge*/  
#include <cstdio>  
#define MT 5  
using namespace std;  
typedef long long ll;  
int prime[] = {2, 3, 7, 61, 24251};  
  
inline ll mulmod(ll a, ll b, ll k) {//*標出了核心語句  
  //  a %= k;  
  //  b %= k;  
    if (b < 0) {///將b變為正的  
        a = -a;  
        b = -b;  
    }  
    ll re = 0, temp = a;  
    ///re為最終結果,temp保持循環.re需要temp的時候,就加一下,否則temp繼續累乘  
    while (b) {  
        if (b & 1) re += temp;///二進制思想,需要即加*  
    //  re %= k;  
        b >>= 1;///下一位等待檢測**  
        temp <<= 1;///temp一直累乘***  
     // temp %= k;  
    }  
    return re%k;*/  
/*實際上呢,用以上的函數在hoj 1356是會TLE的(mod太多次了...)~應該用下面的方法...*/  
    return (a*b)%k;//-_-b  
 }  
//此時不需要再模,於是只剩下核心語句~快速冪和快速積都是二進制思想,核心是一樣的  
inline ll powermod(ll a, ll b, ll k) {  
    ll re = 1, temp = a;  
    while (b) {  
        if (b & 1) re = mulmod(re, temp, k);//只是把"加"入答案變為"乘"入答案  
        temp = mulmod(temp, temp, k);  
        b >>= 1;  
    }  
    return re;  
}  
  
int TwiceDetect(ll a, ll b, ll k) {  
    int t = 0;  
    ll x, y;  
    while ((b & 1) == 0) {  
        b >>= 1;  
        t++;  
    }  
    /// b = d * 2^t;   b = d;  
    y = x = powermod(a, b, k);/// x = y = a^d % n  
    ///二次探測定理是反向遞歸的,當然也可以用如下的正向迭代法去測試  
    while (t--) {  
        y = mulmod(x, x, k);  
        if (y == 1 && x != 1 && x != k - 1)///注意y!=1的時候是不做判斷的,即對應  
            return 0;///遞歸時在某一環節x==p-1的情況,對此x開方則無意義,但是迭代的話不能break,只能ignore並繼續.  
        x = y;///繼續向高次迭代,那么至少最后一次應該是等於1的(Fermat小定理)  
    }  
    return y;  
}  
  
bool Miller_Rabin(ll n) {  
    int i;  
    ll tmp;  
    for (i = 0; i < MT; i++) {  
        tmp = prime[i];  
        if (n == prime[i]) return true;  
        if (TwiceDetect(tmp, n - 1, n) != 1)  
            break;  
    }  
    return (i == MT);  
}  
  
int main() {  
    ll n;  
    while (scanf("%lld", &n) == 1) {  
        if ((n > 1) && Miller_Rabin(n)) {  
            printf("YES\n");  
        } else {  
            printf("NO\n");  
        }  
    }  
    return 0;  
} 
View Code

對於大數的素性判斷,目前Miller-Rabin算法應用最廣泛。一般底數仍然是隨機選取,但當待測數不太大時,選擇測試底數就有一些技巧了。比如,如果被測數小於4 759 123 141,那么只需要測試三個底數2, 7和61就足夠了。當然,你測試的越多,正確的范圍肯定也越大。如果你每次都用前7個素數(2, 3, 5, 7, 11, 13和17)進行測試,所有不超過341 550 071 728 320的數都是正確的。如果選用2, 3, 7, 61和24251作為底數,那么10^16內唯一的強偽素數為46 856 248 255 981。這樣的一些結論使得Miller-Rabin算法在OI中非常實用。通常認為,Miller-Rabin素性測試的正確率可以令人接受,隨機選取k個底數進行測試算法的失誤率大概為4^(-k)。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM