米勒拉宾素数检测
是一种随机化素数检测算法
基于下面的定理
- 费马小定理:如果p是素数,a不是p的倍数,那么\(a ^ {p - 1} \equiv 1(\bmod \ p)\)
- 二次探测定理:如果p是一个素数,且\(x \in [1,p - 1]\),则方程\(x ^2 \% p = 1\)的解为\(x = 1\)或\(x = p - 1\)
费马小定理的逆命题:如果\(a^{p - 1} \equiv 1(\bmod\ p)\)成立,那么\(p\)是一个素数且\(a\)不是\(p\)的倍数
可以确定费马小定理的逆命题不一定成立。
那么对于一个数,如果不满足\(a ^ {p - 1} \equiv 1(\bmod \ p)\)那么一定不是素数,如果满足,那么有可能是素数,取a = [1,p)的随机数,在进行判断,进行几次即可,则为素数的可能性越大
但是对于卡迈克尔数,卡迈克尔数是一个合数\(p,a\)不是\(p\)的倍数,但符合\(a^{p - 1}\equiv 1(\bmod \ p)\)
则需要每次判断费马小定理时,进行二次探测,排除卡迈克尔数
算法
当\(p\)为2时为素数
当\(p\)小于\(2\)或偶数时不是素数
\(10\)次检测,随机化\(a \in [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\n",miller_rabin(n)?"YES":"NO");
return 0;
}
I‘m Stein, welcome to my blog