M i l l e r   R a b i n Miller\ Rabin Miller Rabin判断素数


前置知识:

1.费马小定理: a p − 1 ≡ 1 ( m o d p ) , p a^{p-1}\equiv 1 \pmod p,p ap11(modp)p为质数,且 a a a不为 p p p倍数。

2.二次探测定理:
p p p为素数,则 x 2 ≡ 1 ( m o d p ) x^2\equiv 1\pmod p x21(modp)的解为: x 1 = 1 , x 2 = p − 1 x_1=1,x_2=p-1 x1=1,x2=p1


算法实现流程:

1.特判,当 n < 3 n<3 n<3直接特判,否则 n n n为偶数肯定不是。


2.显然由1筛出来的情况 n − 1 n-1 n1肯定是偶数了。
p = n − 1 = x × 2 c p=n-1=x\times 2^c p=n1=x×2c


3.接下来我们需要利用二次探测定理,选取一个底数 a a a,然后先计算出 a p a^p ap,这里形式就变成了 a p = a n − 1 = a x × 2 c = ( a x ) 2 c a^p=a^{n-1}=a^{x\times 2^c}=(a^x)^{2^c} ap=an1=ax×2c=(ax)2c,然后我们需要循环判断 c c c次,是否满足二次探测定理。


x = a x ( m o d n ) , y = x x=a^x\pmod{n},y=x x=ax(modn),y=x
每次 x = x × x ( m o d n ) = x 2 ( m o d n ) x=x\times x\pmod{n}=x^2\pmod{n} x=x×x(modn)=x2(modn)

然后特判: y 2 = x y^2=x y2=x,当 x = 1 x=1 x=1 y y y是否为 1 1 1 n − 1 n-1 n1

然后一轮过后 y = x y=x y=x,进入下一轮。


4.最后 x = a n − 1 x=a^{n-1} x=an1,利用费马小定理特判 x = 1 x=1 x=1即可。

通过多次测试,使随机算法的错误概率降到很小。


时间复杂度: O ( k l o g 3 n ) O(klog^3n) O(klog3n) k k k为测试次数。

例题:

传送门

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e3+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define fi first
#define se second
#define pb push_back
ll qmul(ll a,ll b,ll mod){
	return (ll)(__int128(a)*b%mod);
}
ll qmul1(ll a,ll b,ll mod){
	ll t=a*b-(ll)((long double)a/mod*b+0.5)*mod;
	return t<0?t+mod:t;
}
ll ksm(ll a,ll n,ll mod){
	ll ans=1;
	while(n){
		if(n&1) ans=qmul1(ans,a,mod);
		a=qmul1(a,a,mod);
		n>>=1;
	}return ans;
}
ll d[8]={2,3,5,7,11,13,79,97};
bool fun(ll n){
	if(n<3) return n==2;
	if(n%2==0) return 0;
	ll p=n-1,c=0;
	while(p%2==0) p>>=1,c++;
	for(int i=0;i<8;i++){
		if(d[i]==n) return 1;
		ll x=ksm(d[i],p,n),y=x;
		for(int j=0;j<c;j++){
			x=qmul1(x,x,n);
			if(x==1&&!(y==1||y==n-1)) return 0;
			y=x;
		}
		if(x!=1) return 0;
	}return 1;
}
int main(){
	ll x;while(~scanf("%lld",&x)){
		puts(fun(x)?"Y":"N");
	}
	return 0;
}