Miller-Rabin素数测试算法

【作用】

有时候我们想快速的知道一个数是不是素数,而这个数又特别的大导致 O( \sqrt\, n ) 的算法不能通过,这时候我们可以对其进行 Miller-Rabin 素数测试,可以大概率测出其是否为素数。

【两个基础理论】

(1)费马小定理:当 p 为质数,有 a^{p-1}\equiv 1(mod \: \, p),不过反过来不一定成立,也就是说,如果 ap 互质,且 a^{p-1}\equiv 1(mod \: \, p),不能推出 p 是质数,比如 Carmichael 数(这个就自行百度吧)

(2)二次探测:如果 p 是一个素数,0 < x < p, 则方程 x^{2}\equiv 1(mod\: \, p) 的解为 x = 1x = p - 1

【两个基本理论的证明】

证明转载费马小定律&二次探测的证明

【算法流程】

(1)对于偶数和 0,1,2 可以直接判断。

(2)设要测试的数为 x,我们取一个较小的质数 a,设 s,t,满足 2^s\cdot t=x-1(其中 t 是奇数)。

(3)我们先算出 a^t,然后不断地平方并且进行二次探测(进行 s 次)。

(4)最后我们根据费马小定律,如果最后 a^{x-1}\not\equiv 1(mod \:\, x),则说明 x 为合数。

(5)多次取不同的 a 进行 Miller-Rabin 素数测试,这样可以使正确性更高

【备注】

(1)我们可以多选择几个 a,如果全部通过,那么 x 大概率是质数。

(2)Miller-Rabin 素数测试中,“大概率”意味着概率非常大,基本上可以放心使用。

(3)当 a 取遍小等于 30 的所有素数时,可以证明 int 范围内的数不会出错。

(4)代码中我用的 int 类型,不过实际上 Miller-Rabin 素数测试可以承受更大的范围。

(5)另外,如果是求一个 long long 类型的平方,可能会爆掉,因此有时我们要用“快速积”,不能直接乘。

【代码】

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int prime[10]={2,3,5,7,11,13,17,19,23,29};
int Quick_Multiply(int a,int b,int c)  //快速积(和快速幂差不多) 
{
    long long ans=0,res=a;
    while(b)
    {
        if(b&1)
          ans=(ans+res)%c;
        res=(res+res)%c;
        b>>=1;
    }
    return (int)ans;
}
int Quick_Power(int a,int b,int c)     //快速幂,这里就不赘述了 
{
    int ans=1,res=a;
    while(b)
    {
        if(b&1)
          ans=Quick_Multiply(ans,res,c);
        res=Quick_Multiply(res,res,c);
        b>>=1;
    }
    return ans;
}
bool Miller_Rabin(int x)     //判断素数 
{
    int i,j,k;
    int s=0,t=x-1;
    if(x==2)  return true;   //2是素数 
    if(x<2||!(x&1))  return false;     //如果x是偶数或者是0,1,那它不是素数 
    while(!(t&1))  //将x分解成(2^s)*t的样子 
    {
        s++;
        t>>=1;
    }
    for(i=0;i<10&&prime[i]<x;++i)      //随便选一个素数进行测试 
    {
        int a=prime[i];
        int b=Quick_Power(a,t,x);      //先算出a^t
        for(j=1;j<=s;++j)    //然后进行s次平方 
        {
            k=Quick_Multiply(b,b,x);   //求b的平方 
            if(k==1&&b!=1&&b!=x-1)     //用二次探测判断 
              return false;
            b=k;
        }
        if(b!=1)  return false;   //用费马小定律判断 
    }
    return true;   //如果进行多次测试都是对的,那么x就很有可能是素数 
}
int main()
{
    int x;
    scanf("%d",&x);
    if(Miller_Rabin(x))  printf("Yes");
    else  printf("No");
    return 0;
}

猜你喜欢

转载自blog.csdn.net/forever_dreams/article/details/82314237