Angular(12) - 新增導航 - 官方教程英雄指南之新增應用內導航 (詳細解說)
用來幹嘛的
要判斷一個數 \(n\) 是否為素數,最樸素直接的辦法是以\(O(\sqrt n)\) 時間複雜度地從2到 \(\sqrt n\) 迴圈即可得到最準確的結果。但是如果在 \(n\) 比較大的情況下,時間花銷就太大了。這時,我們可以選擇犧牲一點點準確度,使用可愛的米勒-拉賓(Miller-Rabin)素性檢驗演算法來判斷質數。根據百度百科,使用快速冪運算,這個演算法的時間複雜度是 \(O(k\log^3 n)\)的,\(k\)是我們設定對一個數的進行測試的次數。\(k\) 越大,判斷錯誤的機率越低,保守估計大概是\(4^{-k}\),實際效果極佳,我們一般取到10就可以了。
誰搞出來的(摘自百度百科)
米勒-拉賓素性檢驗是一種素數判定法則,利用隨機化演算法判斷一個數是合數還是可能是素數。卡內基梅隆大學的計算機系教授Gary Lee Miller首先提出了基於廣義黎曼猜想的確定性演算法,由於廣義黎曼猜想並沒有被證明,其後由以色列耶路撒冷希伯來大學的Michael O. Rabin教授作出修改,提出了不依賴於該假設的隨機化演算法。
要用到的數學定理
費馬小定理:
如果\(p\)是一個質數,而且整數\(a\)與\(p\)互質(即最小公因數\(gcd(a,p) = 1\)),則有\(a^{p-1}≡1(mod~p)\)(模\(p\)同餘符號)。但是這個命題的逆命題不一定能判斷一個數是否為素數,只能說明不滿足\(a^{p-1}≡1(mod~p)\)
證明:不會,感興趣的同學可以自己搜尋相關證明(很多種),用完全剩餘系的證明方法比較容易理解
二次探測定理:
若 \(n\) 為大於2的素數,則對於任意整數 \(a∈[1,n-1]\),使方程\(a^2=1(mod~n)\)成立的解有僅有\(a=1\)或者\(a=n-1\)。在演算法中同樣通過判斷是否可以滿足這個解情況,增強素數判斷的準確性。
證明:還是不會,其實挺好證明的。這位博主的分析比較詳細,可以看看
演算法流程
首先對於一個數 \(num\),先判斷是不是偶數和小於等於2這兩種可以直接篩掉的情況。如果不是,那麼就正式進入判斷流程了。\(num\)
c++程式碼
碼風極醜警告,註釋過多。需要用到快速冪和快速(也叫龜速)乘(不會的同學可以百度一下哦)。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll ;//miller-rabin素數檢驗一般應用於大數的快速檢測,用long long
//快速乘,代替乘法,防止a乘b爆long long
ll qMul(ll a,ll b,ll mod){
ll ans = 0;//a乘b等價轉化為b個a相加,和快速冪原理一致
while(b){
if(b&1) ans = (ans+a)%mod;
a = (a+a)%mod;
b>>=1;
}
return ans;
}
//快速冪模板
ll qPow(ll base,ll power,ll mod){
ll ans = 1;
while(power){
if(power&1) ans = qMul(ans,base,mod);
base = qMul(base,base,mod);
power>>=1;
}
return ans%mod;
}
//miller-rabin素數檢驗函式
bool Miller_Rabin(ll num){
if(num == 2) return true; //2為質數
if(!(num&1)||num<2) return false;//篩掉偶數和小於2的數
ll s = 0,t = num-1; //流程中的s和t,2的s次方*t = num-1
while(!(t&1)){ //當t為偶數的時候,可以繼續分解
s++;
t>>=1;
}
for (int i = 1; i <= 10; i++) { //進行十次測試即可得到比較準確的判斷
ll a = rand()%(num-1)+1; //流程中的隨機整數a,在1到num-1之間
ll x = qPow(a,t,num); //x為二次探測的解
for(int j = 1;j <= s;j++){ //x平方s次可以得到a的num-1次方
ll test = qMul(x,x,num); //test為x平方後對num取模
if(test == 1 && x != 1 && x != num-1) return false; //如果平方取模結果為1,但是作為解的x不是1或者num-1,說明num不是質數,返回
x = test;
}
if(x != 1) return false; //費馬小定理作最後檢測,a的num-1次方對num取模不等於1,一定不是質數
}
return true; //腥風血雨後仍堅持到最後,基本就是真正的質數了
}
int main(){
ll num;
while(cin>>num){
if(Miller_Rabin(num)) cout<<num<<" is a prime."<<endl;
else cout<<num<<" is not a prime."<<endl;
}
return 0;
}
題目
我就是看了這道題才想去學Miller-Rabin素數檢測的(實際上用樸素的方法也能過),用Miller-Rabin可以比樸素的演算法快十倍(如果哪一天被卡了別打我)。感興趣的可以去做一下,搞出迴文數後套Miller-Rabin演算法判斷即可,注意要開long long。
部落格園第一篇博文,謝謝觀看ヾ(≧▽≦*)o,如果覺得有幫助請給我點個小心心 (*>.<*)