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^2\equiv 1\pmod{p}\),那么x=1或x=p−1.

证明:\(x^2\equiv 1\pmod{p}\)可以写作\(x^2-1\equiv 0\pmod{p}\),即\((x+1)*(x-1)\equiv 0\pmod{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\n");
        else printf("%lld\n",ans);
    }
    return 0;
}

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

猜你喜欢

转载自www.cnblogs.com/PPXppx/p/10542703.html