素數測試(Miller-Rabin測試)

思想參照:[http://www.matrix67.com/blog/archives/234
1.素數的個數無限多且不存在最大的素數
證明反證法:假設存在最大的素數P,則我們可以得到一個新的數爲所有的素數向乘加一,即2*3*5*7*……*P+1, 顯然它不能被任一素數整除(所有的素數除它都餘一),這說明我們找到了一個更大的素數。
2.存在任意長的一段連續數,所有數都是合數(相鄰的素數之間的間隔任意大)
證明:當0小於a小於n時n!+a能被a整除。長度爲n-1的序列 n!+2,n!+3,n!+4,…,n!+n中,所有的數都是合數。這個結論對所有大於一的整數n都成立,而n可取到任意大。
3.所有大於二的素數都可以唯一的表示成兩個平方數之差
證明:大於二的素數都是奇數。假設是這個數值2n+1。由於(n+1)^2 = n^2+2n+1,(n+1)^2和n^2就是我們要找的兩個平方數。唯一性:若P能表示成a^2-b^2,則P=a^2-b^2=(a+b)(a-b),由於P、是素數則只能是a-b=1和a+b=P,故解是唯一的。
4.當N爲大於2的整數時, 2^n-1和2^n+1兩個數中如果其中一個是素數那麼另一個肯定是合數
證明:2^n不能被三整除,倘若他被三除餘一那麼2^n-1就能被三整除,如果他被三除餘二,那麼2^n+1就能被三整除,總之,2^n+1 和2^n-1肯定有一個能被三整除。
5.如果p是素數,a是小於p的正整數,則a^(p-1)mod p = 1
首 先我們證明這樣一個結論:如果p是一個素數的話,那麼對任意一個小於p的正整數a,a, 2a, 3a, …, (p-1)a除以p的餘數正好是一個1到p-1的排列。例如,5是素數,3, 6, 9, 12除以5的餘數分別爲3, 1, 4, 2,正好就是1到4這四個數。
反證法,假如結論不成立的話,那麼就是說有兩個小於p的正整數m和n使得na和ma除以p的餘數相同。不妨假設n>m,則p可以整除a(n-m)。但p是素數,那麼a和n-m中至少有一個含有因子p。這顯然是不可能的,因爲a和n-m都比p小。
   用同餘式表述,我們證明了:
  (p-1)! ≡ a * 2a * 3a * … * (p-1)a (mod p)
   也即:
  (p-1)! ≡ (p-1)! * a^(p-1) (mod p)
  兩邊同時除以(p-1)!,就得到了我們的最終結論:
  1 ≡ a^(p-1) (mod p)
Miller-rabin算法:
是一個用來快速判斷一個正整數是否爲素數的算法。

它利用了費馬小定理,即:如果p是質數,且a,p互質,那麼a^(p-1) %p恆等於1。也就是對於所有小於p的正整數a來說都應該符合a^(p-1) % p恆等於1,因爲質數p的歐拉數=p-1。

那麼根據逆否命題,對於一個p,我們只要舉出一個a(a小於p)不符合這個恆等式,則可判定p不是素數。

Miller-rabin算法就是多次用不同的a來嘗試p是否爲素數。
但是每次嘗試過程中還做了一個優化操作,以提高用少量的a檢測出p不是素數的概率。這個優化叫做二次探測。

它是根據一個定理:如果p是一個素數,那麼對於x(0

 #include <iostream>
 using namespace std ;
 #define rd(x) (rand()%(x))
 typedef unsigned long long ll;

 ll pow_mod(ll a,ll b,ll r)
 {
     ll ans=1,buff=a;
     while(b)
     {
         if(b&1)
             ans=(ans*buff)%r;
         buff=(buff*buff)%r;
         b>>=1;
     }
     return ans;
 }

 bool test(ll n,ll a,ll d)
 {
     if(n==2) return true;
     if(n==a) return false;
     if(!(n&1)) return false;
     while(!(d&1)) d>>=1;
     ll t = pow_mod(a,d,n);
     while(d!=n-1&&t!=n-1&&t!=1){
        t = t*t%n;//下面介紹防止溢出的辦法,對應數據量爲10^18次方;
         d<<=1;
     }
     return t == n-1||(d&1)==1;//要麼t能變成n-1,要麼一開始t就等於1
 }

 bool isprime(ll n)
{
    int a[] = {2,3,5,7};//或者自己生成[2,N-1]以內的隨機數rand()%(n-2)+2
     for(int i = 0; i <= 3; ++i){
         if(n==a[i]) return true;
         if(!test(n,a[i],n-1)) return false;
     }
     return true;
 }
 int main()
 {
     int t,ans=0;
     ll N;  
     for(cin >> t;t;t--){
        cin >> N;
         cout << ((isprime(N))?"Yes":"No") <<endl;
  }
}

爲了防止溢出上述的

   ll pow_mod(ll a,ll b,ll r)
    {     ll ans=1,buff=a;
     while(b)
     {
         if(b&1)
             ans=(ans*buff)%r;
         buff=(buff*buff)%r;
         b>>=1;
     }
     return ans;
     }

可以改成:

long long mod_mul(long long a, long long b, long long n) {
    long long res = 0;
    while (b) {
        if(b & 1)
            res = (res + a) % n;
        a = (a + a) % n;
        b >>= 1;
    }
    return res;
}

long long pow_mod(long long a, long long b, long long n) {
    long long res = 1;
    while(b) {
        if(b & 1)
            res = mod_mul(res, a, n);
        a = mod_mul(a, a, n);
        b >>= 1;
    }
    return res;
}

但是複雜度也相應的提高了。

發佈了33 篇原創文章 · 獲贊 2 · 訪問量 7617
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章