是一种随机化素数检测算法
基于下面的定理
- 费马小定理:如果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\n",miller_rabin(n)?"YES":"NO"); return 0; }
来源:https://www.cnblogs.com/Emcikem/p/12321496.html