【洛谷P4718】【模板】Pollard-Rho算法

题目

题目链接:https://www.luogu.com.cn/problem/P4718
Miller Rabin 算法是一种高效的质数判断方法。虽然是一种不确定的质数判断法,但是在选择多种底数的情况下,正确率是可以接受的。
Pollard Rho是一个非常玄学的方式,用于在\(O(n^{1/4})\)的期望时间复杂度内计算合数n的某个非平凡因子。事实上算法导论给出的是\(O(\sqrt p)\)\(p\)\(n\)的某个最小因子,满足\(p\)\(n/p\)互质。但是这些都是期望,未必符合实际。但事实上Pollard Rho算法在实际环境中运行的相当不错。
这里我们要写一个程序,对于每个数字检验是否是质数,是质数就输出Prime;如果不是质数,输出它最大的质因子是哪个。
\(T\leq 350,n\leq 10^{18}\)

思路

嗯好
反正玄学就完事了。
注意光速乘的时候不要 (res%MOD+MOD)%MOD,常数巨大。

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double ld;

const int prm[10]={0,2,3,5,7,11,13,17,19,23};
int Q;
ll n,maxd;

ll fmul(ll x,ll y,ll MOD)
{
	ll z=(ld)x*y/MOD,res=x*y-z*MOD;
	if (res<0) return res+MOD;
	if (res>=MOD) return res-MOD;
	return res;
}

ll fpow(ll x,ll k,ll MOD)
{
	ll ans=1;
	for (;k;k>>=1,x=fmul(x,x,MOD))
		if (k&1) ans=fmul(ans,x,MOD);
	return ans;
}

ll gcd(ll x,ll y)
{
	return y?gcd(y,x%y):x;
}

bool MR(ll n)
{
	for (int i=1;i<=9;i++)
	{
		if (n==prm[i]) return 1;
		if (n<prm[i]) return 0;
		ll m=n-1,power=fpow(prm[i],m,n);
		while (power==1 && !(m&1))
			m>>=1,power=fpow(prm[i],m,n);
		if (power!=1 && power!=n-1) return 0;
	}
	return 1;
}

ll work(ll n)
{
	ll val=1,s=0,t=0,c=rand()%(n-1);
	for (int lim=1,i=1;;i++)
	{
		t=(fmul(t,t,n)+c)%n;
		val=fmul(val,abs(t-s),n);
		if (i==lim || i%127==0)
		{
			ll d=gcd(val,n);
			if (d>1) return d;
			if (i==lim) s=t,val=1,lim<<=1;
		}
	}
}

void PR(ll n)
{
	if (n<=maxd || n<2) return;
	if (MR(n)) { maxd=n; return; }
	ll d=work(n);
	while (d>=n) d=work(n);
	while (n%d==0) n/=d;
	PR(n); PR(d);
}

int main()
{
	scanf("%d",&Q);
	while (Q--)
	{
		scanf("%lld",&n);
		srand(13331U*(Q+2333)*n);
		maxd=0; PR(n);
		if (maxd==n) printf("Prime\n");
			else printf("%lld\n",maxd);
	}
	return 0;
}
posted @ 2021-01-19 15:49  stoorz  阅读(47)  评论(0编辑  收藏  举报