Miller-Rabin 素数判定 与 Pollard Rho

ddl1no2home / 2023-08-24 / 原文

今天打模拟赛的时候没想到 \(T1\) 要这鸟玩意,不会,赛后去学习了一下

参考文献:

warzone大佬

不知道叫什么的大佬

oi-wiki

\(sto\) \(ywc\)大佬 \(orz\)

\(Miller-Rabin\)

这个东西是干嘛的捏。

在一个较为快速的时间内判断一个数是不是质数。

首先我们有两个前置知识。

  • 费马小定理:当 \(p\) 为质数时,\(a^{p-1}\equiv1(mod\ p)\)

  • 二次探测定理:如果 \(p\) 是奇素数,那么如果有 \(x^2\equiv 1(mod\ p)\) ,如果解为 \(\pm 1\) ,那么 \(n\) 大概率为质数,否则 \(n\) 一定为合数。

而其实对于素数判定来说,我们还有费马素数判定。

即倘若 \(a^{p-1}\equiv 1(mod\ p)\) ,那么我们可以猜测很大概率 \(p\) 为质数,当然这也有误差,有一些合数是判不出来的,比如 \(561\) ,所以仅有费马素数判定是不够的,我们还得有其他东西来辅助判断。

而辅助的东西就是 \(Miller-Rabin\)

\(Miller-Rabin\) 需要将费马小定理和二次探测定理结合起来使用。

所以我们先要指导一下为什么二次探测定理是正确的。

二次探测定理证明:

\(x^2\equiv 1(mod\ p)\Rightarrow x^2-1\equiv 0(mod\ p)\Rightarrow (x+1)(x-1)\equiv 0(mod\ p)\)

也就是说 \((x+1)(x-1)\)\(p\) 的倍数,因为 \(p\) 是质数,所以 \(p\%(x+1)\) 不可能为 \(0\) ,而 \(p\%(x-1)\) 不可能为 \(0\) ,所以 \((x+1)(x-1)\) 不可能为 \(p\) 的倍数,所以只有可能 \((x+1)(x-1)=0\)

所以 \(x=\pm 1=1\ or\ p-1\)

证毕。

\(Miller-Rabin\) 实现:

\(a\) 是一个任意底数

\(a^{p-1}\equiv 1(mod\ p)\)

而对于 \(p-1\) 如果 \(p\) 是质数,那么我们只需要对 \(2\) 进行特判以后,那么 \(p-1\) 一定是偶数。

而任意偶数一定可以表示成 \(u\times 2^r\)

\(a^{p-1}=(a^{u})^{2^r}\equiv 1(mod\ p)\)

我们将逐次进行分解枚举,第一次判断 \(a^u\) 第二次是 \(a^{2u}\) 第三次是 \(a^{4u}\) ,也就是外面那层 \(2^r\)

然后我们对于所有的这些都进行判定之后就可以了。

但是这样我们归根结底也只能大概率判断他是一个素数,而不能确定,所以我们需要使用多个底数,然后有一些说法。

一般来说使用 \(2\sim 37\) 的素数判定即可,这样可以正确检验出 \(n\le 10^{18}\) 的质数 。

单次判定时间复杂度为 \(O(k \log^2 n)\)\(k\) 为选择的底数的个数。

还有一些具体的细节在代码里给出:

点击查看代码
int prime[10+10]={2,3,5,7,11,233,331},tim=7;
LL ksm(LL x,LL y,LL MODD) {
	LL ret=1;
	while(y) {
		if(y&1) ret=ret*x%MODD;
		x=x*x%MODD;
		y>>=1;
	}
	return ret;
}
LL mul(LL x,LL y,LL MODD) {
	LL ret=0;
	while(y) {
		if(y&1) ret=(ret+x)%MODD;
		x=(x+x)%MODD;
		y>>=1;
	}
	return ret;
}
bool miller_rabin(LL a,LL n) {
	LL s=n-1,r=0;
	while(!(s&1)) {
		s>>=1; 
		++r;
	}
	LL x=ksm(a,s,n);//先求出a^u
	for(int i=0;i<r;++i) {//每次翻倍
		LL tmp=mul(x,x,n);//太大的时候用龟速乘,如果不用的话时间复杂度降到 $O(klogn)$
		if(x!=1&&x!=n-1&&tmp==1) return false;
		//将上一个x,作为底,如果他的平方为1,且他自己不为1,就不符合二次探测
		x=tmp;
	}
	return (x==1);//费马判定
}
bool check(int n) {
	if(n==1) return false;//特判
	for(int i=0;i<tim;++i) {
		if(n==prime[i]) return true;//特判
		if(!miller_rabin(prime[i],n)) {
			return false;
		}
	}
	return true;
}

\(Pollard\ Rho\)

咕咕咕