Miller-Rabin 素數檢驗演算法
演算法簡介
Miller-Rabin演算法,這是一個很高效的判斷質數的方法,可以在用\(O(logn)\) 的複雜度快速判斷一個數是否是質數。它運用了費馬小定理和二次探測定理這兩個篩質數效率極高的方法。
費馬小定理判質數
\(a^{p - 1}\ ≡\ 1\ mod\ p\)
這個定理在 \(p\) 為質數的時候是成立的,所以我們可以如果要判斷 \(p\) 是否是質數,可以 \(rand\) 幾個 \(a\) 值然後照著這個式子來算,如果算出來不是 \(1\) 那說明 \(p\) 一定不是質數。
但在我們的自然數中,如果照著這個式子算出來的答案為1,也是有可能不是質數的。更有一類合數,它用費馬小定理不管 rand 什麼數都判不掉。這類合數稱為 Carmichael數(
二次探測定理
因為Carmichael數的存在,使得我們難以高效判斷質數,所以我們還需要加入第二種判斷方法使這種偽演算法更優秀!而二次探測無疑就是為我們量身定製的演算法,因為它要建立在同餘式右邊為1的基礎上(而我們的費馬小定理不正好滿足了要求嗎?)
若 \(b^2≡1\ mod\ p\) 且 \(p\) 為質數 \(=>\) 則 \(p\) 一定可以被 \(b−1\) 和 \(b+1\) 其中一個整除
這是二次探測定理,原理很簡單,我們將上面的同餘式左右都減1,根據平方差公式可以得出 \((b−1)(b+1)≡\ 0\ mod\ p\)
根據這個道理,我們可以進行二次探測:因為 \(a^{p−1}≡1\mod\ p\) 如果 \(p−1\) 為偶數的話就可以化成: \(a^{(\frac{p−1}2)^2}≡1\ mod\ p\) 這樣就變成了二次探測的基本式。
typedef long long ll; typedef unsigned long long ull; typedef long double lb; inline ll ksc(ull x, ull y, ll p) { // O(1)快速乘(防爆long long) return (x * y - (ull)((lb)x / p * y) * p + p) % p; } inline ll ksm(ll x, ll y, ll p) { //快速冪 ll res = 1; while (y) { if (y & 1) res = ksc(res, x, p); x = ksc(x, x, p); y >>= 1; } return res; } inline bool mr(ll x, ll p) { if (ksm(x, p - 1, p) != 1) return 0; //費馬小定理 ll y = p - 1, z; while (!(y & 1)) { //一定要是能化成平方的形式 y >>= 1; z = ksm(x, y, p); //計算 if (z != 1 && z != p - 1) return 0; //不是質數 if (z == p - 1) return 1; //一定要為1,才能繼續二次探測 } return 1; } inline bool prime(ll x) { if (x < 2) return 0; if (x == 2 || x == 3 || x == 5 || x == 7 || x == 43) return 1; return mr(2, x) && mr(3, x) && mr(5, x) && mr(7, x) && mr(43, x); }
這樣子加上二次探測之後,明顯就能高效很多,基本上卡不了,大概要每 \(10^{10}\) 個數才會出現一個判不掉的,這個概率可以說十分微小,可以忽略!