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;
}
posted @ 2023-08-24 20:23  s1monG  阅读(122)  评论(0编辑  收藏  举报