Miller-Rabin素數檢測演算法
阿新 • • 發佈:2020-07-19
由於收到某退役學長的鞭策,忽然就想學習一丟數論
來補充一下虎哥基礎數論中沒有出現的東西
**本文轉載須聯絡作者,並標明出處**
---
# 定義
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}$ 是真的好看
感覺這篇部落格寫的很認真,希望能自我提升,也能幫到大家~~