米勒拉宾素数检测

是一种随机化素数检测算法

基于下面的定理

  • 费马小定理:如果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;
}
posted @ 2020-02-17 13:52  Emcikem  阅读(465)  评论(0编辑  收藏  举报