快速判断素数(素数测试)(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}\)

浙公网安备 33010602011771号