1. 程式人生 > 其它 >快速判斷素數(素數測試)(Miller-Rabin測試)

快速判斷素數(素數測試)(Miller-Rabin測試)

快速判斷素數(素數測試)(Miller-Rabin測試)

可以知道目前最快的準確判斷素數的演算法就是\(\mathcal{O(n)}\)的尤拉篩了

但是這是對\(n\)個質數的判斷,但是我的值域如果變大

那麼我們需要一個不依賴篩法的判斷方法

首先,\(Fermat\)小定理,也就是說滿足\(a^{n-1} \equiv 1 (mod n)\)

大部分是質數,但是也有反例,於是這個演算法被拋棄了

然後搞出來一個\(Miller-Rabin\)測試

是在上面那個定理的基礎上演變而來的

還有另外一個結論:如果\(n\)是質數的話,x是小於\(n\)的整數,且\(x^2 \equiv 1(mod n)\)

那麼\(x\)要麼等於\(1\),要麼等於\(n-1\),好像這個是顯然的

於是我們可以不斷提取\(n-1\)\(2\)因子,最後在平方回去

我們設\(n-1=d*2^r\)\(d\)是奇數

\(x^d\%mod\)要麼最初始的時候就等於\(1\),要麼在平方的過程中會變成\(n-1\).

於是這個素數測試法就誕生了,出錯概率極小

程式碼中\(isp\)返回是否為素數,是的話返回\(true\)

code
int ksm(int x,int y,int mod){
    int ret=1;
    while(y){
        if(y&1)ret=ret*x%mod;
        x=x*x%mod;y>>=1;
    }
    return ret;
}
int jd[10]={0,2,7,61};//自行更改
bool jud(int x,int a,int y){
    if(x==2)return true;
    if(x^1)return false;
    while(y^1)y>>=1;
    int pw=ksm(a,y,x);
    while(y!=x-1&&pw!=x-1&&pw!=1)
        pw=pw*pw%x,y<<=1;
    return pw==x-1||(y&1)==1;
}
bool isp(int x){
    fo(i,1,3){
        if(x==jd[i])return true;
        if(!jud(x,jd[i],x-1))return false;
    }
    return true;
}

下面的話粘自這裡

對於大數的素性判斷,目前\(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}\)