Pollard-Rho算法andMiller_Rabin算法

洛咕

题意:对于每个数字n((n<=10^{18}))检验是否是质数,是质数就输出Prime;如果不是质数,输出它最大的质因子.

分析:Pollard-Rho算法模板题.

Pollard-Rho算法适用于大整数的分解质因数.这是一个很玄学的算法,理论复杂度为(N^{1/4}),实际复杂度靠人品,但总的说来它的实际复杂度还是很优秀的.

在学习Pollard-Rho算法之前,还要学习一个同样玄学的Miller_Rabin算法,该算法适用于大整数的质数判定.因为我们要分解一个整数N,首先要确保它不是质数.

Miller_Rabin算法

首先根据费马小定理:若p是质数,且gcd(a,p)=1,那么 (a^{p-1}≡1(mod p)).我们可以通过这个定理,直接快速幂来判断该数是否为质数.然后费马小定理只是质数的性质之一,不代表满足这个性质就一定是质数(理解充分必要条件).例如(2^{340}≡1(mod 341)),但是341=11∗31不是质数.

于是又有了二次探测定理:若p为质数,且(x^2equiv 1pmod{p}),那么x=1或x=p−1.

证明:(x^2equiv 1pmod{p})可以写作(x^2-1equiv 0pmod{p}),即((x+1)*(x-1)equiv 0pmod{p}),即(p|(x+1)*(x-1)).因为p是一个质数,所以(p=x+1)(p=x-1(mod p)),所以(x=p-1)(x=p+1=1(mod p)).

对于一个数我们先跑费马小定理,若满足费马,再二次探测,此时准确率已经很高了,因为我们无法证明这个Miller_Rabin算法是百分百准确率(实际上好像也达不到),所以只能说它是一个高效的随机算法.代码实现:

inline bool mr(LL x,LL p){
    if(ksm(x,p-1,p)!=1)return 0;//费马
    rg LL k=p-1;
    while(k%2==0){
		k>>=1;//x^(p-1)变成(x^((p-1)/2))^2
    	LL t=ksm(x,k,p);//二次探测
		if(t!=1&&t!=p-1)return 0;
		if(t==p-1)return 1;
//t=1时还能够继续探测,
//而t=p-1时无法继续探测,只好认为该数是质数
    }
    return 1;//通过了所有检验,只好认为该数是质数
}

回到正题Pollard-Rho算法,我们判完了不是质数之后,就开始考虑质因数分解.不想讲了

这个算法每个人的模板都不一样.然后洛咕上这道题精心卡常爆long long,所以用到了__int128还有一些优化.

#include<bits/stdc++.h>
#define rg register
#define LL long long 
using namespace std;
inline LL read(){
    rg LL s=0,w=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){s=s*10+ch-'0';ch=getchar();}
    return s*w;
}
LL T,n,ans;
inline LL ksm(LL a,LL b,LL c){
    rg LL cnt=1;
    while(b){
		if(b&1)cnt=(__int128)(cnt*a)%c;
		a=(__int128)(a*a)%c;
		b>>=1;
    }
    return cnt%c;
}
inline bool mr(LL x,LL p){//Miller_Rabin算法
    if(ksm(x,p-1,p)!=1)return 0;
    rg LL k=p-1;
    while(k%2==0){
		k>>=1;LL t=ksm(x,k,p);
		if(t!=1&&t!=p-1)return 0;
		if(t==p-1)return 1;
    }
    return 1;
}
inline bool pd_prime(LL n){
    if(n==2||n==7||n==19||n==61||n==73)return true;
    return mr(2,n)&&mr(7,n)&&mr(19,n)&&mr(61,n)&&mr(73,n);
}//随机选几个质数来判定n是不是质数.
inline LL gcd(LL a,LL b){
    if(b==0)return a;
    return gcd(b,a%b);
}
inline LL pr(LL n){//Pollard-Rho算法
    rg LL x,y,cs,sum,i,j,d;
    while(1){
		y=x=rand()%n;cs=rand()%n;
//算法中的那个随机数公式y=(x*x+cs)%n
		sum=1;//超级大优化,下面讲
        i=0;j=1;//i一步一步走,j乘2乘2走,判环
		while(++i){
	    	x=((__int128)x*x+cs)%n;
	    	sum=(__int128)abs(y-x)*sum%n;
//我们把所有y-x的乘积存到sum里,最后一起与n求gcd
//这样是不影响正确性的(证明略)
//试想如果每一次都gcd,肯定会超时
	    	if(x==y||sum==0)break;
//x=y说明走到了环
	    	if(i%127==0||i==j){
//i%127==0指每找到127个(y-x)求一次gcd
//127可以随意指定.
				d=gcd(sum,n);
				if(d>1&&d!=n)return d;
				if(i==j)y=x,j<<=1;
//如果i追上了j,则j要乘2往前走了.
//y记录x这一次的位置,上面更新完x之后,如果x=y就是环
	    	}
		}
    }
}
inline void find(LL n){
    if(n<=ans)return;//最优性剪枝
    if(pd_prime(n)){ans=n;return;}
    rg LL p=pr(n);while(n%p==0)n/=p;
    find(p);find(n);
}//因为本题要找最大的质因子,就需要find函数来递归
int main(){
    T=read();
    while(T--){
		ans=1;n=read();find(n);
		if(ans==n)printf("Prime
");
		else printf("%lld
",ans);
    }
    return 0;
}

如果学会了exgcd,还可以去做[CQOI2016]密钥破解这道题,保证就是Pollard-Rho和exgcd的模板.

原文地址:https://www.cnblogs.com/PPXppx/p/10542703.html