基础素数测试模板
对于大数的素性判断,目前Miller-Rabin算法应用最广泛。一般底数仍然是随机选取,但当待测数不太大时,选择测试底数就有一些技巧了。比如,如果 被测数小于4759123141,那么只需要测试三个底数 a[]={2,7,61} 就足够了。当然,测试的越多,正确的范围也越大。如果你每次都用前7个素数 a[]={2,3,5,7,11,13,17} 进行测试,所有不超过341550071728320的数都是正确的。如果选用 a[]={2,3,7,61,24251} 作为底数,那么10^16内唯一的强伪素数为46856248255981。这样的一些结论使得Miller-Rabin算法在OI中非常实用。通常认为,Miller-Rabin素性测试的正确率可以令人接受,随机选取k个底数进行测试算法的失误率大概为4^(-k)。
tip:1无法进行判断,只能自行特判为false!
#includeusing namespace std ; typedef long long ll; ll pow_mod(ll a,ll b,ll r) { ll ans=1,buff=a; while(b) { if(b&1) ans=(ans*buff)%r; buff=(buff*buff)%r; b>>=1; } return ans; } bool test(ll n,ll a,ll d) { if(n==2)return true; if(n==a)return false; if(!(n&1))return false; while(!(d&1))d>>=1; ll t=pow_mod(a,d,n); while(d!=n-1&&t!=n-1&&t!=1) { t=t*t%n; d<<=1; } return t==n-1||(d&1)==1;//要么t能变成n-1,要么一开始就t=1 } bool isprime(ll n) { int a[]={2,3,5,7}; //看情况取值 for(int i=0;i<=3;i++) { if(n==a[i])return true; if(!test(n,a[i],n-1))return false; } return true; } int main() { int t; ll n; for(cin>>t;t;t--) { cin>>n; cout<<((isprime(n))?"Yes":"No")<<endl; } return 0; }
ps:注意上述算法中的幂运算是longlong类型,longlong×longlong肯定会出现溢出现象,如果不会java大整数,手里也没有大整数乘法模板的话,有一个小技巧可以避免溢出,方法就是乘法改为加法,把上面的代码:
ll pow_mod(ll a,ll b,ll r) { ll ans=1,buff=a; while(b) { if(b&1) ans=(ans*buff)%r; buff=(buff*buff)%r; b>>=1; } return ans; }
改为:
ll mod_mul(ll a,ll b,ll n) { ll res=0; while(b) { if(b&1) res=(res+a)%n; a=(a+a)%n; b>>=1; } return res; } ll pow_mod(ll a,ll b,ll n) { ll res=1; while(b) { if(b&1) res=mod_mul(res,a,n); a=mod_mul(a,a,n); b>>=1; } return res; }