Miller-Rabin 算法
引入
用途
用于快速的判断一个数是否为质数。
费马素性测试
费马小定理:若 \(p\) 为质数,那么 \(a^{p-1}=1\pmod p\),
我们只需枚举 \(a=2\sim p-1\) ,检验 \(a^{p-1}=1\pmod p\) 是否满足即可。
然而一些合数也可以通过这个测试,比如 \(561=3\times 11\times 17\).
二次探测定理
若 \(p\) 是奇素数,那么若 \(x^2=1\pmod p\),则 \(x=p-1\pmod p\),或 \(x=1\pmod p\).
平方差公式即可。
Miller-Rabin 素性测试
我们不妨把上述两者结合起来。
我们每轮测试有一个数 \(a\).
若 \(a^{p-1}=1\pmod p\),我们继续检验 \(a^{p-1}\) 的平方根。
直到不存在平方根为止,或平方根变成了 \(p-1\)。
一直开方的过程中,若存在一个平方根不为 \(p-1\) 或 \(1\),则称不通过这场测试,即必为合数。
我们可以这样实现,设 \(n-1=u\times 2^t\),先求 \(a^u\),然后不断平方,最后到达 \(a^{n-1}\).
若 \(a^u=1\),那么之后都是 \(1\),那么就通过。
否则不断平方,若出现了一个 \(n-1\),那么也通过了。
若一直没有出现 \(n-1\),因为最后一定会变成 \(1\),
则代表出现非平凡的平方根,那么不通过。
至于每轮测试的数 \(a\),我们可以随机。
但是我们也可以使用 \(2\sim 37\) 共 \(12\) 个质数检验,这样可以保证 \(n\le 10^{18}\) 里所有数都是对的。
code
#include<bits/stdc++.h>
#define LL long long
#define ull unsigned long long
using namespace std;
int p[12]={2,3,5,7,11,13,17,19,23,29,31,37};
LL mul(LL a,LL b,LL mod) {
LL tmp=(long double)a/mod*b;
LL res=(ull)a*b-(ull)tmp*mod;
return (res+mod)%mod;
}
LL qpow(LL a,LL b,LL mod) {
LL res=1;
while(b) {
if(b&1) res=mul(res,a,mod);
a=mul(a,a,mod);
b=b>>1;
}
return res;
}
bool Miller_Rabin(LL n) {
if(n<=2) return n==2;
if(n<=37) {
for(int i=0; i<12; i++)
if(n==p[i]) return true;
return false;
}
LL u=n-1,t=0;
while(u%2==0) u/=2,t++;
for(int i=0; i<12; i++) {
LL v=qpow(p[i],u,n);
if(v==1) continue;
LL s=0;
for(; s<t; s++) {
if(v==n-1) break;
v=mul(v,v,n);
}
if(s==t) return false;
}
return true;
}
int main() {
LL n; cin>>n;
if(Miller_Rabin(n)) puts("Yes");
else puts("No");
return 0;
}