Miller-Rabin素性測試

原文地址:https://www.cnblogs.com/Norlan/p/5350243.html

素數:若一個數x的約數僅僅只有1和他本身,則稱之爲素數,注意,1不是素數。

素數的性質:

1.素數無窮多,不存在最大素數

證明:假設最大素數存在且爲p,我們可以用舊素數去構造新素數,設x=2*3*5*7*11*......*p+1,x不會被用來構造他的任何一個素數整除,當然由於素數之間也是互質的,沒有被用來構造他的素數也不能整除他,只不過用來構造他的素數整除他的餘數爲1罷了。

2.存在任意長的一段連續區間,區間內的數全部都是合數

證明:當0<a<=n時,n!+a%a=0。而n!+2,n!+3,n!+4,......,n!+n都是合數(可分別被2,3,4,......,n整除),我們構造出了一段n-1的連續合數區間。

注:相鄰素數之間的間隔可以是任意的可能是有問題的說法,通過上述的構造方法,我們可以構造出一個連續合數區間,使得它的長度>=n-1,但是,具體這個長度是多少是未知的。以N=5爲例,N!+2,N!+3,N!+4,N!+5,都是合數,但是N!+1以及N!+6都是合數,不再往兩邊繼續舉例,因此這個辦法無法證明相鄰素數間隔任意。

3.所有大於2的素數都可以唯一表示成兩個平方數之差

證明:首先我們知道,大於2的素數都是奇數,那麼我們假設這個數爲2*n+1。(n+1)^2=n^2+2*n+1,而2*n+1=(n+1)^2-n^2。

唯一性:若素數p=a^2-b^2,那麼p=(a+b)*(a-b),由於p是素數,那麼只能是a+b=p且a-b=1。

4.當n爲大於2的整數時,2^n+1和2^n-1這兩個數,至少有一個是合數

證明:2^n%3!=0。分情況討論:

若2^n%3=1,那麼2^n-1%3=0,爲合數。

若2^n%3=2,那麼2^n+1%3=0,爲合數。

5.費馬小定理:如果p是素數,a是小於p的正整數,那麼a^(p-1)%p=1

證明:1.如果p是素數,那麼對於任意一個小於p的正整數a,有a、2a、3a、4a、......、(p-1)a,這些數分別取餘p後的結果是一個1~p-1的全排列。舉個例子:5是素數,3, 6, 9, 12除以5的餘數分別爲3, 1, 4, 2。

反證法:如果結論不成立,我們可以知道有兩個小於p的數m和n使得m*a%p=n*a%p。假設m>n,我們可以構造一個

(m-n)*a%p=0,由於p是素數,只可能(m-n)%p=0或者a%p=0,但顯然的是由於m-n和a都是小於p的,因此不成立。

那麼由上述結論我們可以得到a*2a*......*(p-1)a%p=(p-1)!,兩邊同時除去(p-1)!,即可得到a^(p-1)%p=1。

下面進入素性測試部分:

由費馬小定理我們可以知道:如果p是素數,a是小於p的正整數,那麼a^(p-1)%p=1。

費馬素性測試:如果我們反過來考慮,如果a是小於p的正整數,a^(p-1)%p=1,是否可以得到p是素數呢。

以a=2時爲例,2^4%5=1,2^340%341=1,5爲素數但是341=11*31。顯而易見的是,如果a^(p-1)%p!=1,那麼可以直接肯定p不可能爲素數,而能通過這種測試的我們稱之爲以2爲底的僞素數。

那麼自然會聯想到,如果用所有小於p的數去測試p的素性呢。然而數據表明,有些合數強大到可以通過所有的這種測試。前10億個自然數中有600個之多。

Miller-Rabin素性測試:

原理:如果p是素數,x是一個小於p的正整數,若x^2%p=1,我們可以推出(x-1)(x+1)%p=0,那麼x的取值只能是1或者p-1。

具體測試方法:以341爲例:

A.由於2^{340} (mod) 341 = 1,我們假設341爲素數,那麼我們可以得到結論:2^{170}(mod)341=1或者2^{170}(mod)341=340

B.進行求證,如果滿足2^{170}(mod)341=1(正確),繼續按照分解的原理求證2^{85}(mod)341=1或者2^{85}(mod)341=340

如果2^{170}(mod)341=340,我們是不可以繼續往下分解的,那麼我們就可以判定341是以2爲底的強僞素數。

C.我們發現2^{85}(mod)341=32,所以可以得到結論341並不是素數。

這種測試方法的出錯概率比費馬素性測試要低得多,數據表明:第一個以2爲底的強僞素數爲2047。第一個以2和3爲底的強僞素數則大到1373653。

具體代碼(核心部分):

#include <iostream>
using namespace std ;
typedef long long ll;

ll pow_mod(ll a,ll x,ll n){//快速冪取模
    ll r=1;
    while(x){
        if(x&1){
            r=r*a%n;
        }
        a=a*a%n;
        x>>=1;
    }
    return r;
}

bool test(ll a,ll x,ll n){//a^x%n  x=n-1
    if(!(n&1))return false;//大於2的偶數不可能是素數
    while(!(x&1))x>>=1;//將x的所有2全部提取
    ll res=pow_mod(a,x,n);
    while(x!=n-1&&res!=n-1&&res!=1){
        res=res*res%n;
        x<<=1;
    }//3*3%5==4  
    return res==n-1||(x&1)==1;
    //從小求到大可以減少運算
    //如果說res==1,並且n要爲素數,那麼必須在x還爲奇數也就是第一次進行素性判斷的時候,res==1才行
    //由梅氏素性測試的原理,若t*t%n==1,則t==1或t==n-1
    //梅氏素性測試要求如果t*t%n=1的時候  才能去判定是否由t==1或者t==n-1
    //如果遇到了t*t%n=n-1的情況,此時是無法向下繼續判斷的,因此按照素性測試的要求,我們直接返回是強僞素數即可
    //t%n=n-1時,t*t%n=1,往後都是1
    //代碼是從小判到大的,強僞素數要求要麼一開始t就是1要麼t在變換中途變成了n-1
}

bool isPrime(ll n){
    int a[]={2,3,5,7};
    for(int i=0;i<=3;i++){
        if(n==a[i])return true;
        if(!test(a[i],n-1,n))return false;//未通過以2 3 5 7四個數中任意一個爲底時的素性測試
    }
    return true;
}

int main(){
    ll ans;
    cin>>ans;
    cout<<(isPrime(ans)?"Yes":"No");
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章