是一种随机化素数检测算法
基于下面的定理
- 费马小定理:如果p是素数,a不是p的倍数,那么(a ^ {p - 1} equiv 1(mod p))
- 二次探测定理:如果p是一个素数,且x∈[1,p - 1],则方程(x ^2 \% p = 1)的解为(x = 1)或(x = p - 1)
费马小定理的逆命题:如果(a^{p - 1} equiv 1(mod p))成立,那么p是一个素数且a不是p的倍数
可以确定费马小定理的逆命题不一定成立。
那么对于一个数,如果不满足(a ^ {p - 1} equiv 1(mod p))那么一定不是素数,如果满足,那么有可能是素数,取a = [1,p)的随机数,在进行判断,进行几次即可,则为素数的可能性越大
但是对于卡迈克尔数,(卡迈克尔数是一个合数p,a不是p的倍数,但符合a^{p - 1}equiv 1(mod p))
则需要每次判断费马小定理时,进行二次探测,排除卡迈克尔数
算法
当p为2时为素数
当p小于2或偶数时不是素数
10次检测,随机化(a∈[1,p))求出(a^u \% n),然后二次探测判断
其中进行优化,使得进行二次探测时的幂小一点,然后在慢慢扩大,不然,如果直接计算p - 1次幂,可能会造成溢出的情况
#include <iostream>
#include <cstdio>
#include <cstdlib>
#define ll long long
using namespace std;
ll mod_mul(ll a,ll b,ll p){//a * b % p
ll ans = 0;
while(b){
if(b & 1)ans = (ans + a) % p;
b >>= 1;
a = (a + a) % p;
}
return ans;
}
ll mod_exp(ll a,ll b,ll p){
ll ans = 1;
while(b){
if(b & 1)ans = mod_mul(ans,a,p);//a和b可能很大
b >>= 1;
a = mod_mul(a,a,p);
}
return ans;
}
bool miller_rabin(ll n){
if(n == 2)return 1;
if(n < 2 || !(n & 1))return 0;
ll u,t;
for(t = 0,u = n - 1; !(u & 1); t++,u>>=1);//n-1 =u*2^t
for(int i = 0; i < 10; i++){//10次试探
ll a = rand() % (n - 1) + 1;//a∈[1,n)
ll x = mod_exp(a,u,n);
for(int j = 0; j < t; j++){//二次试探
ll y = mod_mul(x,x,n);
if(y == 1 && x != 1 && x != n - 1)
return 0;
x = y;//相当于把幂变回到p - 1
}
if(x != 1)return 0;
}
return 1;
}
int main(){
srand(time(NULL));
int n;
scanf("%d",&n);
printf("%s
",miller_rabin(n)?"YES":"NO");
return 0;
}