[hiho第92周]Miller-Rabin素性测试的c++实现
证明:
如果n是素数,整数$a$ 与$n$ 互素,即$n$ 不整除$a$ ,则${a^{n - 1}} \equiv 1(\bmod n)$ ,如果能找到一个与$n$ 互素的整数$a$ ,是的上式不成立,则可以断定$n$ 是合数,反之则不成立,这类合数我们称之为Carmichael数。当上式成立时,称$n$ 为以$a$ 为底的伪素数。
以上测试素数的方法称为fermat测试。
Miller-Rabin素性检验是在上面的基础上加上一个二次探测定理。
强伪素数:设$n - 1 = {2^s}t$ ,$2\nmid t$ ,$b$ 与$n$ 互素。若${b^t} \equiv 1(\bmod n)$ 或存在$r$ , $0 \le r \le s$ 使得${b^{{2^r}t}} \equiv - 1(\bmod n)$ ,则称n为以b为底的强伪素数。
当$n$ 为素数时,他一定是从任何数$b$ 为基的强伪素数,以$b$为基的强伪素数一定是以$b$为基的伪素数。
二次探测定理:如果p是奇素数,则 ${x^2} \equiv 1(\bmod p)$ 的解为$x \equiv 1$ 或 $x \equiv p - 1(\bmod p)$
如果${a^{n - 1}} \equiv 1(\bmod n)$成立,Miller-Rabin算法不是立即找另一个$a$进行测试,而是看$n-1$ 是不是偶数。如果$n-1$ 是偶数,另$u = \frac{{n - 1}}{2}$,并检查是否满足二次探测定理即${a^u} \equiv 1$或${a^u} \equiv n - 1(\bmod n)$。若不满足,则为合数。
定理:若n是奇合数,则在区间$0 < b < n$ 中,最多有25%的数$b$ ,能使$n$ 是以$b$ 为基的强伪素数。
所以,结果的正确率为$1 - \frac{1}{{{4^k}}}$
复杂度:$O(S\log n)$
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<cstdlib> 5 #include<iostream> 6 #include<cmath> 7 typedef long long ll; 8 using namespace std; 9 const int S=20; 10 ll mod_mul(ll a,ll b,ll p){ 11 ll res=0; 12 a%=p,b%=p; 13 while(b){ 14 if(b&1)res=(res+a)%p; 15 a=(a<<1)%p; 16 b>>=1; 17 } 18 return res; 19 } 20 ll mod_pow(ll x,ll n,ll p){ 21 ll res=1; 22 while(n){ 23 if(n&1)res=mod_mul(res,x,p); 24 x=mod_mul(x,x,p); 25 n>>=1; 26 } 27 return res; 28 } 29 30 bool check(ll a,ll n,ll x,ll t){//判断是否为合数 31 ll ret=mod_pow(a,x,n); 32 ll last=ret; 33 for(int i=1;i<=t;i++){ 34 ret=mod_mul(ret,ret,n); 35 if(ret==1&&last!=1&&last!=n-1)return 1; 36 last=ret; 37 } 38 if(ret!=1) return 1;//fermat测试 39 return 0; 40 } 41 42 bool Miller_Rabin(ll n){ 43 if(n<2)return 0; 44 if(n==2)return 1; 45 if((n&1)==0)return 0; 46 ll x=n-1,t=0; 47 while((x&1)==0)x>>=1,t++; 48 for(int i=0;i<S;i++){ 49 ll a=rand()%(n-1)+1; 50 if(check(a,n,x,t))return 0;//合数 51 } 52 return 1; 53 } 54 55 int main(){ 56 ll t,n; 57 scanf("%lld",&t); 58 while(t--){ 59 scanf("%lld",&n); 60 if(Miller_Rabin(n))printf("Yes\n"); 61 else printf("No\n"); 62 } 63 return 0; 64 }