1. 程式人生 > >Miller-Rabin素數檢測演算法

Miller-Rabin素數檢測演算法

由於收到某退役學長的鞭策,忽然就想學習一丟數論 來補充一下虎哥基礎數論中沒有出現的東西 **本文轉載須聯絡作者,並標明出處** --- # 定義 Miller-Rabin素數測試,又稱米勒-拉賓素性檢驗,是一種素數判定法則,利用隨機化演算法判斷一個數是合數還是可能是素數。 卡內基梅隆大學的計算機系教授Gary Lee Miller首先提出了基於廣義黎曼猜想的確定性演算法,由於廣義黎曼猜想並沒有被證明,其後由以色列耶路撒冷希伯來大學的Michael O. Rabin教授作出修改,提出了不依賴於該假設的隨機化演算法。(摘自百度百科) # 用處&背景 根據上面的定義可以顯然的看到,這個演算法的主要目的就是進行單個素數的判定 在前期學習當中,我們也學習過單個素數的判定 複雜度為$O(\sqrt n)$,程式碼如下 ```cpp bool isPrime(int x) { if (x < 2) return false; for (int i = int(sqrt(x+0.5)); i >= 2; --i) { if (x % i == 0) return false; } return true; } ``` 那麼利用Miller-Rabin(簡稱MR)演算法 還有優秀的龜速乘(快速加)以及快速冪 複雜度可以達到$O(klog_n)$ MR的複雜度在百科中給出了一大堆$log$像這樣: 使用快速傅立葉變換能夠將這個時間推進到$O(klog_nloglog_nlogloglog_n)=O(klog_n)$ 總之複雜度就是$O(klog_n)$ 而且正確性也有一定的保障 經過證明(我不會) 每次檢測MR給出的錯誤結果的概率小於等於$\frac 1 4$ 那麼進行k次檢測的錯誤概率可降低至$O({\frac 1 4}^k)$ 實際使用效果要比理論值好不少 可以說是相當優秀了 # 證明 下面來看正確性的證明 需要用到的前置知識:**費馬小定理**,**二次探測定理**,**Wilson定理**。 不太好解釋,沒關係,我們一個一個來看 有個別不懂的演算法可以直接點選右側目錄去看 ## 費馬小定理 ### 性質 若a,p互質,則$a^{p-1}≡1(mod p)$ ### 證明 考慮$1,2,3...(p - 1)$共$p-1$個數字,給所有數字同時乘$a$,得到$a,2a,3a,...(p - 1)a$ $$\because a \neq b (mod p), (c, p) = 1$$ $$\therefore ac \neq bc(mod p) $$ $$\therefore 1*2*3...(p - 1) \equiv a*2a*3a...(p-1)a (mod p) $$ $$\therefore (p-1)! \equiv (p-1)!a^{p-1}(mod p)$$ $$\because ((p-1)!, p) \equiv 1 $$ $$\therefore a^{p-1} \equiv 1(mod p)$$ ## 二次探測定理 ### 性質 如果$p$是一個素數,且$0 #include #include #include #define int long long using namespace std; int prime[10]={2,3,5,7,11,13,17,19,23,29}; inline int Quick_Multiply(int a, int b, int c){ //快速乘 int ans = 0; while(b){ if(b & 1) ans = (ans + a) % c; a = (a + a) % c; b >>= 1; } return ans; } inline int Quick_Power(int a, int b, int c){ //快速冪 int ans = 1; while(b){ if(b & 1) ans = Quick_Multiply(ans, a, c); a = Quick_Multiply(a, a, c); b >>= 1; } return ans; } inline bool Miller_Rabin(int x){ //判斷素數 int i, j, k; int s = 0, t = x - 1; if(x == 2) return true; //2是素數 if(x < 2 || !(x & 1)) return false; //如果x是偶數或者是0,1,那它不是素數 while(!(t & 1)){ //將x分解成(2^s)*t的樣子 s++; t >
>= 1; } for(i = 0; i < 10 && prime[i] < x; ++i){ //隨便選一個素數進行測試 int a = prime[i]; int b = Quick_Power(a, t, x); //先算出a^t for(j = 1; j <= s; ++j){ //然後進行s次平方 k = Quick_Multiply(b, b, x); //求b的平方 if(k == 1 && b != 1 && b != x - 1) //用二次探測判斷 return false; b = k; } if(b != 1) return false; //用費馬小定律判斷 } return true; //如果進行多次測試都是對的,那麼x就很有可能是素數 } signed main(){ int x; scanf("%lld", &x); if(Miller_Rabin(x)) printf("Yes\n"); else printf("No\n"); return 0; } ``` --- ## 總結 終於寫完了,一晚上就學習了這一個演算法 $Markdown$敲起來真費勁,但是 $L_{a}$${T_ex}$ 是真的好看 感覺這篇部落格寫的很認真,希望能自我提升,也能幫到大家~~