Miller-Rabin算法本質上是一種概率算法,存在誤判的可能性,但是出錯的概率非常小。出錯的概率到底是多少,存在嚴格的理論推導。
一、費馬小定理
假如p是質數,且gcd(a,p)=1,那么 a(p-1)≡1(mod p)
如果存在a<p,且a(p-1) % p != 1,則p肯定不是素數。
二、有限域上的平方根定理
三、Miller-Rabin算法
對於一個大數n,判斷n是不是素數的時候,可以先考慮a(n-1)≡ 1(mod n)
對於n-1,一定可以拆分成2s+d:
可以從x = ad開始,依次平方s次,每次平方的時候模上n,按照之前的平方根定理,如果模上n的結果為1的話,那么x一定是1,或者是n-1,如果不滿足則不是素數,x=x2,再次循環。
每次隨機選一個在2-n-1的數字作為a,可以重復測試。
由於mod上的是n,n是一個大數,所以快速冪中的乘法,需要用快速加法來實現。不然就算模上之后再相乘也會溢出。
1 #include<iostream> 2 #include<ctime> 3 #include<algorithm> 4 using namespace std; 5 typedef long long ll; 6 const int maxn = 1000000+10; 7 ll mul(ll a, ll b, ll m) 8 //求a*b%m 9 { 10 ll ans = 0; 11 a %= m; 12 while(b) 13 { 14 if(b & 1)ans = (ans + a) % m; 15 b /= 2; 16 a = (a + a) % m; 17 } 18 return ans; 19 } 20 ll pow(ll a, ll b, ll m) 21 //a^b % m 22 { 23 ll ans = 1; 24 a %= m; 25 while(b) 26 { 27 if(b & 1)ans = mul(a, ans, m); 28 b /= 2; 29 a = mul(a, a, m); 30 } 31 ans %= m; 32 return ans; 33 } 34 bool Miller_Rabin(ll n, int repeat)//n是測試的大數,repeat是測試重復次數 35 { 36 if(n == 2 || n == 3)return true;//特判 37 if(n % 2 == 0 || n == 1)return false;//偶數和1 38 39 //將n-1分解成2^s*d 40 ll d = n - 1; 41 int s = 0; 42 while(!(d & 1)) ++s, d >>= 1; 43 srand((unsigned)time(NULL)); 44 for(int i = 0; i < repeat; i++)//重復repeat次 45 { 46 ll a = rand() % (n - 3) + 2;//取一個隨機數,[2,n-1) 47 ll x = pow(a, d, n); 48 ll y = 0; 49 for(int j = 0; j < s; j++) 50 { 51 y = mul(x, x, n); 52 if(y == 1 && x != 1 && x != (n - 1))return false; 53 x = y; 54 } 55 if(y != 1)return false;//費馬小定理 56 } 57 return true; 58 } 59 int main() 60 { 61 int T; 62 cin >> T; 63 ll n; 64 while(T--) 65 { 66 cin >> n; 67 if(Miller_Rabin(n, 50))cout<<"Yes"<<endl; 68 else cout<<"No"<<endl; 69 } 70 }


