快速判断素数(素数测试)(Miller-Rabin测试)

快速判断素数(素数测试)(Miller-Rabin测试)

可以知道目前最快的准确判断素数的算法就是\(\mathcal{O(n)}\)的欧拉筛了

但是这是对\(n\)个质数的判断,但是我的值域如果变大

那么我们需要一个不依赖筛法的判断方法

首先,\(Fermat\)小定理,也就是说满足\(a^{n-1} \equiv 1 (mod n)\)

大部分是质数,但是也有反例,于是这个算法被抛弃了

然后搞出来一个\(Miller-Rabin\)测试

是在上面那个定理的基础上演变而来的

还有另外一个结论:如果\(n\)是质数的话,x是小于\(n\)的整数,且\(x^2 \equiv 1(mod n)\)

那么\(x\)要么等于\(1\),要么等于\(n-1\),好像这个是显然的

于是我们可以不断提取\(n-1\)\(2\)因子,最后在平方回去

我们设\(n-1=d*2^r\)\(d\)是奇数

\(x^d\%mod\)要么最初始的时候就等于\(1\),要么在平方的过程中会变成\(n-1\).

于是这个素数测试法就诞生了,出错概率极小

代码中\(isp\)返回是否为素数,是的话返回\(true\)

code
int ksm(int x,int y,int mod){
    int ret=1;
    while(y){
        if(y&1)ret=ret*x%mod;
        x=x*x%mod;y>>=1;
    }
    return ret;
}
int jd[10]={0,2,3,7,61};
bool jud(int x,int a,int y){
    if(x==2)return true;
    if(!(x&1))return false;
    while(!(y&1))y>>=1;
    int pw=ksm(a,y,x);
    while(y!=x-1&&pw!=x-1&&pw!=1)
        pw=pw*pw%x,y<<=1;
    return (pw==x-1)||((y&1)==1);
}
bool isp(int x){
    if(!x||x==1)return false;
    fo(i,1,4){
        if(x==jd[i])return true;
        if(!jud(x,jd[i],x-1))return false;
    }
    return true;
}

下面的话粘自这里

对于大数的素性判断,目前\(Miller-Rabin\)算法应用最广泛

一般底数仍然是随机选取,但当待测数不太大时,选择测试底数就有一些技巧了

比如,如果被测数小于\(4 759 123 141\),那么只需要测试三个底数\(2,7,61\)就足够了

当然,你测试的越多,正确的范围肯定也越大

如果你每次都用前\(7\)个素数\((2,3,5,7,11,13,17)\)进行测试,所有不超过\(341 550 071 728 320\)的数都是正确的

如果选用\(2,3,7,61,24251\)作为底数,那么\(10^{16}\)内唯一的强伪素数为\(46 856 248 255 981\)

这样的一些结论使得\(Miller-Rabin\)算法在\(OI\)中非常实用

通常认为,\(Miller-Rabin\)素性测试的正确率可以令人接受

随机选取 k个底数进行测试算法的失误率大概为\(4^{-k}\)

posted @ 2021-12-20 19:15  fengwu2005  阅读(693)  评论(0)    收藏  举报