有关素数的研究很久之前就已经开始,根据科(xuan)学研究,数质数有助于睡眠。那么如何高效的让计算机数质数,跑得更快?这就是我们要探讨的主要内容。
数据比较小的时候,O( n√ )判断法就够了。方法就是从2枚举到 n√ 。(顺便吐槽一句数学必修三居然枚举到n-1,差评)
实现:
bool isPrime(int x){
if(x<2) return false;
for(int i=2;i*i<=m;i+=(i==2?1:2))
if(m%i==0) return false;
return true;
}
有一个神奇的定理叫费马小定理。如果一个奇素数p,有 ap≡a(mod p) ,其中 1⩽a<p
它的逆命题是错误的,但是如果我们rand()很多数出来带进逆命题检验,结果如何?事实证明,随机数目在1000次左右时,准确率可以达到99.7%左右。实现:
#include
using namespace std;
typedef long long LL;
int m[]={3,7,13,37,23};
LL ksm(LL a,LL m,LL n){
if(m==0) return 1;
if(m==1) return a%n;
LL w=ksm(a,m>>1,n);
w=w*w%n;
if(m&1) w=w*a%n;
return w;
}
bool fm(LL n){
if(n==2) return true;
for(int i=0;i<5;++i){
int a=m[i];
if(ksm(a,n,n)!=a) return false;
}
return true;
}
int main(){
LL n;
scanf("%d",&n);
if(fm(n)) printf("Yes\n");
else printf("No\n");
return 0;
}
我闲来无事将这个代码和直接判定对拍了一下,卡在了2821上。为什么呢?
有一类特殊的数:Carmichael数。这类数的特征是,对 ∀ a< p上述性质都是成立的,这其中最小的是561.
因此,费马小定理测试得到了毁灭性打击。不过这仍然不失为一种容易实现的判断方法万一出题人卡这东西呢?说的好像因为出题人可能卡SPFA你就不用了似的为此而开发出的一种新的判断方法,就是Miller-Rabin测试。
即便对RP的需要有所下降,这仍然是一个拼RP的做法,但是你没有选择。因为这是最为快速且高效的判断法。
Miller-Rabin筛法基于Miller-Rabin定理。
Miler-Rabin定理
若n为素数,取 a<n ,设 n−1=d⋅2r ,
要么 ad≡1(mod n) ,要么 ∃0⩽i⩽r,使ad⋅2i≡−1(mod n)
该定理的逆命题是不成立的,但是如果我们多取几个素数,逆命题正确的概率就会大大上升。与费马小定理不同之处是,MR定理不存在合数满足这一性质。
如果取的是2,3,5,7这四个,而在 2.5×1013 以内只有3215031751这个数会测试失误。
代码参考了
https://github.com/Psycho7/Miller-Rabin/blob/master/CPP/Miller-Rabin.cpp
在下找了半天之后认为写的比较精悍的一个
#include
using namespace std;
typedef long long LL;
int pri[]={2,3,5,7,11,13,17};
LL ksm(LL a,LL n,LL mod){
if(n==0) return 1;
if(n==1) return a%mod;
LL ans=1;
while(n){
if(n&1) ans=(ans*a)%mod;
a=a*a%mod;
n>>=1;
}
return ans;
}
bool MR(LL x){
if(x==2) return true;
if((x<2)||!(x&1)) return false;
LL d=x-1;
while(!(d&1))d>>=1;
LL td,t;
for(int i=0;i<7;++i){
if(pri[i]>=x) break;
td=d;
t=ksm(pri[i],td,x);
while((td!=x-1)&&(t!=1)&&(t!=x-1)){
t=t*t%x;
td<<=1;
}
if((t==x-1)||(td&1)) ;
else return false;
}
return true;
}
int main(){
LL k;
cin>>k;
cout<<(MR(k)?"Yes":"No");
}
说实话我是不太懂这个代码究竟是如何实现的,所以实在不行就先背会吧。
基本上相信自己的rp和出题人的良心的话跑费马小定理测试一般不会出问题,毕竟家里那本书在MR测试下堂而皇之的写着费马测试的代码,实在是不太想吐槽。当然,数据比较小的话,朴素算法也是没有什么问题的,关键是不容易出错,不要把8个TLE变成20个WA比什么都强。
复杂度的话,费马是O(klogn)的,MR是O( klog3n )(别吐槽这个鬼畜的三次方……),别忘了一定要写快速幂。