大素数测试的Miller-Rabin算法
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 }
越努力,越幸运