Loading

Pollard-Rho 算法

Miller-Rabin 素性检测

部分内容摘自 题解 P4718/论 Miller-Rabin 算法的确定性化 - It's LUNATIC time!)

根据费马小定理,若 \(p\) 为素数,那么对于 \(1 \leq a < p\),都有 \(a^{p-1} \equiv 1 \pmod p\)

因此,若存在 \(1 \leq a < p\) 使得 \(a^{p-1} \not\equiv 1 \pmod p\),那么 \(p\) 一定不是素数。

于是有费马素性检测:在 \([1,p)\) 中随机选取几个 \(a\),然后使用快速幂来检测是否有 \(a^{p-1} \equiv 1 \pmod p\)。如果对于每个选取的 \(a\)\(p\) 都通过了检测,那么费马素性检测认为 \(p\) 是素数。

但是有的合数 \(p\) 满足对于 \(1 \leq a < p\),都有 \(a^{p-1} \equiv 1 \pmod p\)。这些数被称为卡迈克尔数。因此,我们需要检测 \(p\) 的更多性质。

二次探测定理:若 \(p\) 为奇素数,则 \(x^2 \equiv 1 \pmod p\) 的解为 \(x \equiv \pm 1 \pmod p\)

\(p\) 为奇数,显然 \(p-1\) 是偶数。首先令 \(d = p-1\),然后判断 \(a^d \bmod p\) 是否为 \(1\),若不是则 \(p\) 为合数。接下来,若 \(d\) 为偶数则令 \(d\) 除以 \(2\),然后重复上述判断,直到 \(d\) 为奇数或 \(a^d \not\equiv 1 \pmod p\)

如果最后一步中 \(a^d \not \equiv \pm 1 \pmod p\),则 \(p\) 一定为合数,否则 Miller-Rabin 素性检测认为它是素数。

事实上只需要选择前 \(12\) 个素数为底数就可以适用于 \(2^{78}\) 内的素性检测。

struct MillerRabin{
	int prime[20]={2,3,5,7,11,13,17,19,23,29,31,37};
	inline bool test(int v,ll p){
		ll d=p-1,cur=qpow(v,d,p);
		if(cur!=1) return false;
		while(!(d&1)){
			d>>=1,cur=qpow(v,d,p);
			if(cur==p-1) return true;
			else if(cur!=1) return false;
		}
		return true;
	}
	inline bool check(ll x){
		if(x<=40){
			for(int i=0;i<12;++i) if(x==prime[i]) return true;
			return false;
		}
		for(int i=0;i<12;++i) if(!test(prime[i],x)) return false;
		return true;
	}
}T;

Pollard-Rho 算法

Pollard-Rho 算法用于在 \(O(n^{1/4})\) 的复杂度计算合数 \(n\) 的某个非平凡因子。

考虑一个随机函数 \(f(x) = (x^2+c) \bmod n\),那么取 \(f\) 的很多项相邻作差求绝对值,很大概率能够取到一个数和 \(n\) 的 GCD 不是 \(1\)

我们倍增一下。每次取 \([2^k,2^{k+1})\) 这一段,设段头的 \(f(x)\) 值为 \(s\),这一段里面其它的值为很多个 \(t\),那么每次把 \(|s - t|\) 乘起来对 \(t\) 取模,如果乘了 \(127\) 次就取一下模,然后求一遍 GCD。

反正这是玄学,它能找就当它能找吧。

# include <bits/stdc++.h>
// # define int long long
const int N=100010,INF=0x3f3f3f3f;

typedef __int128 ll;

std::mt19937 rng(time(0));

inline ll qpow(ll d,ll p,ll MOD){
	ll ans=1;
	while(p){
		if(p&1) ans=ans*d%MOD;
		d=d*d%MOD,p>>=1;
	}
	return ans;
}

struct MillerRabin{
	int prime[20]={2,3,5,7,11,13,17,19,23,29,31,37};
	inline bool test(int v,ll p){
		ll d=p-1,cur=qpow(v,d,p);
		if(cur!=1) return false;
		while(!(d&1)){
			d>>=1,cur=qpow(v,d,p);
			if(cur==p-1) return true;
			else if(cur!=1) return false;
		}
		return true;
	}
	inline bool check(ll x){
		if(x<=40){
			for(int i=0;i<12;++i) if(x==prime[i]) return true;
			return false;
		}
		for(int i=0;i<12;++i) if(!test(prime[i],x)) return false;
		return true;
	}
}T;
ll res;

inline ll read(void){
	ll res,f=1;
	char c;
	while((c=getchar())<'0'||c>'9')
		if(c=='-') f=-1;
	res=c-48;
	while((c=getchar())>='0'&&c<='9')
		res=res*10+c-48;
	return res*f;
}

inline ll f(ll x,ll c,ll n){
	return (x*x+c)%n;
}
inline ll myabs(ll x){
	return (x>0)?x:-x;
}
inline ll gcd(ll a,ll b){
	return (!b)?a:gcd(b,a%b);
}
inline ll PollardRho(ll x){ // 有的时候会有 s = t,此时成环了,不需要继续找下去
	ll s=0,t=0,val=1,c=rng()%(x-1)+1,d;
	for(int ga=1;;ga<<=1,s=t,val=1){
		for(int cur=1;cur<=ga;++cur){
			t=f(t,c,x),val=myabs(s-t)*val%x;
			if(cur==127){ 
				d=gcd(val,x);
				if(d>1) return d;
			}
		}
		d=gcd(val,x);
		if(d>1) return d;
	}
	assert(false);
	return 114514;
}
inline void print(ll x){
	if(x<0) putchar('-'),x=-x;
	if(x>9) print(x/10);
	putchar(x%10+'0');
	return;
}

inline void solve(ll x){
	if(x==1) return;
	if(T.check(x)){
		res=std::max(res,x);
		return;
	}
	ll cur=x;
	while(cur>=x) cur=PollardRho(x);
	while(x%cur==0) x/=cur;
	solve(cur),solve(x);
	return;
}


signed main(void){
	int T=read();
	while(T--){
		ll n=read();
		res=0,solve(n);
		if(res==n) puts("Prime");
		else print(res),puts("");
	}
	return 0;
}
posted @ 2023-10-11 15:51  Meatherm  阅读(35)  评论(1编辑  收藏  举报