Miller-Rabin判素法

身败名裂的阮行止这篇文章出错了。

注意下面介绍的算法只是伪素数测试

59eb6654682d2.png (230×230)
给定一个数n,判断它是否是质数n<=2×109.
回顾以前判断素数的办法。我们把n[1,n]的范围内试除,看是否有数能整除n。算法的时间复杂度是O(n)的。这里跑不过。

费马小定理

nZ+p为质数,有npn(modp).
更一般的欧拉定理gcd(p,n)=1,有nφ(p)1(modp).
考虑费马小定理的逆命题
npn(modp),则p为质数。
这个命题显然是错的,但是它几乎成立。因此我们可以用它来判定素数。

伪素数判断

随机选择x,如果xnx(modn),那么n肯定不是质数。
因为有可能出错,所以我们选择多个底数来测试。如果所有的测试都认为n是质数,那么我们就认为它是质数。
这样写:
bool is_prime(int x)
{
    int i,x;
    for(i=0;i<lambda;i++)
    {
        x=rand()%n+1;
        if((pow(x,n)%n)!=x) return 0;       //发现不符合
    }
    return 1;
}
lambda是测试次数。这个东西设为多少看数据范围。
最终代码:
#define lambda 233

long long pow(long long a,long long b,long long mod)       
{
    long long tmp;
    if(b==0) return 1;
    if(b==1) return a;

    tmp=pow(a,b/2,mod);
    if(b%2==0) return tmp%mod*tmp%mod; //b是偶数
    else return tmp%mod*tmp%mod*a%mod;     //b是奇数
}//快速幂,计算a^b对mod取模的结果

bool is_prime(long long n)
{
    long long i,x;

    for(i=0;i<lambda;i++)
    {
        x=rand()%(n-1)+1;               //随便选个[1,n-1]的数
        if(pow(x,n,n)!=x) return 0;     //如果x^n模n的结果不是n,则n必不是质数
    }

    return 1;
}//质数返回1,合数返回0

复杂度讨论

这东西的复杂度是多少呢?
对于每一次测试x,快速幂计算xn将会耗费logn的时间。
那么总的时间复杂度就是λlogn

tyvj P4192 素数测试

给定一个n,判断n是否为质数。
Code
#include <cstdio>
#include <cstdlib>

#define lambda 10

long long pow(long long a,long long b,long long mod)       //快速幂,计算a^b对mod取模的结果
{
    long long tmp;
    if(b==0) return 1;
    if(b==1) return a;

    tmp=pow(a,b/2,mod);
    if(b%2==0) return tmp%mod*tmp%mod; //b是偶数
    else return tmp%mod*tmp%mod*a%mod;     //b是奇数
}

bool is_prime(long long n)
{
    long long i,x;

    for(i=0;i<lambda;i++)
    {
        x=rand()%(n-1)+1;               //随便选个[1,n-1]的数
        if(pow(x,n,n)!=x) return 0;     //如果x^n模n的结果不是n,则n必不是质数
    }

    return 1;
}

int main()
{
    long long x;
    scanf("%lld",&x);
    printf("%s\n",Miller_Rabin(x)==0?"No":"Yes");
}
由于tyvj数据很水,lambda取1(只测试一次)都能过……

update on 2016.8.31
关于出错
经过证明,如果是真的Miller-Rabin算法,取前二十个质数为底数来测验,可以保证在int范围内不出错。

评论

此博客中的热门博文

将博客部署到星际文件系统(IPFS)

高中地理必修一知识点总结

一场CF的台前幕后(下)