快速判斷素數(素數測試)(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}\)