Miller-Rabin随机法判定大质数

在测试一些非常大的数时,如果用根号n的方法判断也会耗掉大量时间,实际上我们有更快的方法——Miller-Rabin随机法

我们知道,费马小定理:假如p是质数,且**(a,p)=1**,那么a^(p-1)≡1(mod p)。即假如p是质数,且a,p互质,那么a的(p-1)次方除以p的余数恒等于1。

在大多数情况下费马小定理的逆定理都是成立的,那么我们就得到了一个定理的直接应用,对于待验证的数p,我们不断取a∈[1,p-1]且a∈Z,验证a^(p-1) mod p是否等于1,不是则p果断不是素数,共取s次。其中a^(p-1) mod p可以通过把p-1写成二进制,由(a*b)mod c=(a mod c)*b mod c,可以在t=log(p-1)的时间内计算出解,如考虑整数相乘的复杂度,则一次计算的总复杂度为log³(p-1)。这个方法叫快速幂取模。

我们还有这样一个定理:对于0<x<p,x^2 mod p =1 => x=1或p-1

我们令p-1=(2^t )*u,即p-1为u二进制表示后面跟t个0。我们先计算出x[0]=a^u mod p ,再平方t次并在每一次模p,每一次的结果记为x[i],最后也可以计算出a^(p-1) mod p。若发现x[i]=1而x[i-1]不等于1也不等于p-1,则发现p必然不是素数。

通过这个两个定理的应用,出错率大大降低,可以说基本不出错。

模板:

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL n;
inline LL mul(LL a,LL b,LL p)//快速乘
{
    
    
    LL ans=0;
    while(b)
    {
    
    
        if(b&1) ans=(ans+a)%p;
        a=(a+a)%p;
        b>>=1;
    }
    return ans;
}
inline LL poww(LL x,LL num,LL p)//快速幂
{
    
    
    LL sum=1,k=x%p;
    while(num)
    {
    
    
        if(num&1) sum=mul(sum,k,p);
        num=num>>1;
        k=mul(k,k,p);
    }
    return sum;
}
inline bool miller_rabin(long long n)//miller_rabin算法,随机法判断大质数
{
    
    
    if(n==2) return true;
    if(n<2||!(n&1)) return false;
    LL t=0,a,x,y,u=n-1;
    while(!(u&1)) t++,u>>=1;//若u为偶数,则右移一位,用t记录位数
    for(int i=0;i<=20;i++)
    {
    
    
        a=rand()*rand()%(n-1)+1;
        x=poww(a,u,n);
        for(int j=0;j<t;j++)//把移掉的位补回来
        {
    
    
            y=mul(x,x,n);
            if(y==1&&x!=1&&x!=n-1) return false;//二次探测
            x=y;
        }
        if(x!=1) return false;//费马小定理
    }
    return true;
}
int main()
{
    
    
    scanf("%I64d",&n);
    printf("%d",miller_rabin(n));
    system("pause");
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Stevenwuxu/article/details/112968300