zoukankan      html  css  js  c++  java
  • 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的模板.

  • 相关阅读:
    《C#多线程编程实战》2.7 CountDownEvent
    《C#多线程编程实战》2.6 ManualResetEventSlim
    《C#多线程编程实战》2.5 AutoResetEvent
    《C#多线程编程实战》2.4 SemaphoreSlim
    ConcurrentDictionary与Dictionary 替换
    vs2017 代码格式化 文档排版 编辑 设置文档的格式
    面试笔记
    way.js
    SQL Server 2008 技巧快捷键
    CentOS下Docker与.netcore(四)之 三剑客之一Docker-machine+jenkins简单自动化部署
  • 原文地址:https://www.cnblogs.com/PPXppx/p/10542703.html
Copyright © 2011-2022 走看看