如果要判断一个比较大的数是否为素数,那么此时传统的试除法和筛法显然不再适用,我们引入一种概率型素数判定方法——Miller-Rabin素数测试。
由费马小定理可以知道,若n是素数且a是整数,则满足a^n≡a(mod n).若存在a不满足上式,那么n是合数。由此我们定义伪素数:令a是一正整数,若n是合数且满足a^n≡a(mod n),那么n称为以a为基的伪素数。
Miller-Rabin素数测试基于费马小定理:假如n是素数且gcd(a,n)=1,那么a^(n-1)≡1(mod n).如果a^(n-1)≡1(mod n)(a为任意小于n的正整数),则可近似认为n为素数,取多个底进行试验,次数越多,n为素数的概率越大。
定义卡迈克尔数:一个合数n,若对于所有满足gcd(b,n)=1的正整数b都有b^(n-1)≡1(mod n)成立,则称之为卡迈克尔数.
为了排除卡迈克尔数导致Miller-Rabin测试出现错误,我们引进二次探测定理:如果p是素数且0<x<p,则方程x^2%p=1的解为x=1或x=p-1.
改进后的Miller-Rabin素数测试实现代码如下:
#include <cstdio> #include <iostream> #include <cstdlib> using namespace std; #define N 10 typedef long long LL; LL random(LL n) { return (LL)((double)rand()/RAND_MAX*n+0.5); } LL multi(LL a,LL b,LL m) //计算a*b%m { LL ret=0; while(b) { if(b&1) ret=(ret+a)%m; b>>=1; a=(a<<1)%m; } return ret; } LL quick_mod(LL a,LL b,LL m) //计算a^b%m { LL ans=1; while(b) { if(b&1) { ans=multi(ans,a,m); b--; } b>>=1; a=multi(a,a,m); } return ans; } bool miller_rabin(LL n) { if(n==2) return true; if(n<2||!(n&1)) return false; LL m=n-1; int k=0; while((m&1)==0) { k++; m>>=1; } for(int i=0; i<N; i++) { LL a=rand()%(n - 1)+1; LL x=quick_mod(a,m,n); LL y=0; for(int j=0;j<k;j++) { y=multi(x,x,n); if(y==1&&x!=1&&x!=n-1) return false; x=y; } if(y!=1) return false; } return true; } int main() { LL n; while(cin>>n) if(miller_rabin(n)) puts("yes"); else puts("no"); return 0; }