適用范圍:較大數的較快素性判斷
思路:
因為有好的文章講解具體原理(見參考文章),這里只是把代碼的大致思路點一下,讀完了文章如果還有些迷糊,可以參考以下解釋
原理是費馬小定理:如果p是素數,則a^(p-1)%p==1,加上二次探測定理:如果p是一個素數,則x^2%p==1的解為,則x=1或者x=n-1。
因為有通過費馬小定理的偽素數的概率不是充分小,在此基礎上加以改進判斷。
一次檢測中:
主要是把一個數n的n-1分解成d*2^r的形式,其中d為奇數,正向過程是a^n%p如果是1,就繼續分解a^(n/2)%p,(a為一個與n互素的數)看是否為1,;如果是n-1就停止分解,說明至此無法判斷是否為素數;如果不等於這兩個值,則一定為合數。而在寫代碼過程是這個過程的逆向過程,先分解到底,看最后這個a^d%p是否為1或n-1,如果是說明已經分解到底了,也就是通過了此次素性測試。如果不是,說明在正向過程中出現了要么a的某次方為n-1,根據算法停止了檢測過程;要么就是中間的某一個結果不等於這兩個數,那么就是合數。就從最后往前面推,每一步看滿不滿足上述條件。直到判斷為合數或者終止檢測的那一步。
多次檢測過程:
不停更換a測試。
代碼:(代碼中可能需要用到快速冪和大數乘積取余,可以參考前一篇博客)
1 #include <iostream> 2 #include <time.h> 3 using namespace std; 4 long long an[] = {2,3,5,7,11,13,17,61}; 5 long long Random(long long n)//生成0到n之間的整數 6 { 7 return (double) rand()/RAND_MAX*n+0.5;//(doubel)rand()/RAND_MAX生成0-1之間的浮點數 8 } 9 long long q_mod(long long a,long long n,long long p) 10 { 11 a = a%p; 12 //首先降a的規模 13 long long sum = 1;//記錄結果 14 while(n) 15 { 16 if(n&1) 17 { 18 sum = (sum*a)%p;//n為奇數時單獨拿出來乘 19 } 20 a = (a*a)%p;//合並a降n的規模 21 n /= 2; 22 } 23 return sum; 24 } 25 long long q_mul(long long a,long long b,long long p) 26 { 27 long long sum = 0; 28 while(b) 29 { 30 if(b&1)//如果b的二進制末尾是零 31 { 32 (sum += a)%=p;//a要加上取余 33 } 34 (a <<= 1)%=p;//不斷把a乘2相當於提高位數 35 b >>= 1;//把b右移 36 } 37 return sum; 38 } 39 bool witness(long long a,long long n) 40 { 41 long long d = n-1; 42 long long r = 0; 43 while(d%2==0) 44 { 45 d/=2; 46 r++; 47 }//n-1分解成d*2^r,d為奇數 48 long long x = q_mod(a,d,n); 49 //cout << "d " << d << " r " << r << " x " << x << endl; 50 if(x==1||x==n-1)//最終的余數是1或n-1則可能是素數 51 { 52 return true; 53 } 54 while(r--) 55 { 56 x = q_mul(x,x,n); 57 if(x==n-1)//考慮開始在不斷地往下余的過程 58 { 59 return true;//中間如果有一個余數是n-1說明中斷了此過程,則可能是素數 60 } 61 } 62 return false;//否則如果中間沒有中斷但最后是余數又不是n-1和1說明一定不是素數 63 } 64 bool miller_rabin(long long n) 65 { 66 const int times = 50;//試驗次數 67 if(n==2) 68 { 69 return true; 70 } 71 if(n<2||n%2==0) 72 { 73 return false; 74 } 75 for(int i = 0;i<times;i++) 76 { 77 long long a = Random(n-2)+1;//1到(n-1) 78 //cout << a << endl; 79 if(!witness(a,n)) 80 { 81 return false; 82 } 83 } 84 return true; 85 } 86 int main() 87 { 88 long long num; 89 cin >> num; 90 if(miller_rabin(num)) 91 { 92 cout << "Yes" << endl; 93 } 94 else 95 { 96 cout << "No" << endl; 97 } 98 }
參考文章:
Matrix67,數論部分第一節:素數與素性測試,http://www.matrix67.com/blog/archives/234(原理只推薦這一篇,這一篇是我目前見到的解釋的最清晰,也可能是最精彩的,沒有之一!雖然是07年的,好博客與時間沒有關系)
因為上篇代碼部分用的是Pascal,這里找到c++的代碼版本:
StanleyClinton,素數判定Miller_Rabin算法詳解,https://blog.csdn.net/maxichu/article/details/45458569
還有rand函數的使用:https://jingyan.baidu.com/article/e73e26c060bdbc24adb6a7b0.html