1. 程式人生 > 實用技巧 >Angular(12) - 新增導航 - 官方教程英雄指南之新增應用內導航 (詳細解說)

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)\)

條件的 \(p\) 一定是合數。在本演算法裡,主要就是運用了它的逆命題來檢驗素數的。

證明:不會,感興趣的同學可以自己搜尋相關證明(很多種),用完全剩餘系的證明方法比較容易理解

二次探測定理:

​  若 \(n\) 為大於2的素數,則對於任意整數 \(a∈[1,n-1]\),使方程\(a^2=1(mod~n)\)成立的解有僅有\(a=1\)或者\(a=n-1\)。在演算法中同樣通過判斷是否可以滿足這個解情況,增強素數判斷的準確性。

證明:還是不會,其實挺好證明的。這位博主的分析比較詳細,可以看看

演算法流程

​  首先對於一個數 \(num\),先判斷是不是偶數和小於等於2這兩種可以直接篩掉的情況。如果不是,那麼就正式進入判斷流程了。\(num\)

必為奇數,則\(num-1\)一定是個偶數,而偶數可以分解為\(2^s \cdot t = num-1\)的形式。這裡如果我們讓兩邊作為一個整數\(a\)的指數,不就可以利用費馬小定理\(a^{num-1}≡1(mod~num)\)來檢驗 \(num\) 是否為素數了嗎?別急,在算出 \(a^{2^s \cdot t}\) 的過程中,我們可以順便利用二次探測定理來檢測,大大提高我們判斷的準確度。我們的做法是先隨機產生一個比 \(num\) 小的整數 \(a\) ,先計算出\(a^t\) ,在我下面的程式碼中把這個值記作 \(x\)。然後迴圈 \(s\) 次,每次都用一個變數 \(test\) 記錄 \(x^2\)\(num\) 取模的值,如果 \(test = 1\)則說明\(x^2=1(mod~num)\)成立,進而可以判斷 \(x\) 是否為1或者\(num-1\) ,如果\(x\) 都不是則說明 \(num\) 肯定不是素數啦。反覆運用 \(s\) 次二次探測定理,最後再判斷一次\(a^{2^s \cdot t}≡1(mod~num)\)是否成立,如果過了最後費馬小定理這關,恭喜這個 \(num\) 經過了第一層考驗。我們對 \(num\) 進行 \(k\) 次這樣的考驗,每次取一個不同的 \(a\) ,如果始終沒有返回 ,則說明 \(num\) 最終通過了 \(Miller\) 測試。

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;
}

題目

牛客NC14703素數迴文

​  我就是看了這道題才想去學Miller-Rabin素數檢測的(實際上用樸素的方法也能過),用Miller-Rabin可以比樸素的演算法快十倍(如果哪一天被卡了別打我)。感興趣的可以去做一下,搞出迴文數後套Miller-Rabin演算法判斷即可,注意要開long long。

部落格園第一篇博文,謝謝觀看ヾ(≧▽≦*)o,如果覺得有幫助請給我點個小心心 (*>.<*)