Miller Rabin and Pollard Rho算法学习笔记 大佬们的博客 Some Links

学习笔记:

神仙算法,学了我两天,好多地方的博客虽然内容没有讲错,但是逻辑却有一点小问题,误导了我好久。。。

Miller_Rabin:
  1. 首先我们要会判断质数,最古老的枚举显然在这里不够用,所以我们要用到,费马小定理,即 a p 1 1 mod   p ,同时还要求 a , p 互质。
  2. 但是这还不够,因为有不是素数的数也满足这个条件,所以我们还要用一次二次探测定理,即若 p 为素数且 a 2 1 mod p 则有 a 1 mod p a 1 mod p ,证明的话把 1 移到左边去平方差一下然后用唯一分解定理即可。
  3. 但是这样还会极小的例外,这时为了避免这种人品上的问题,别忘了最开始筛一下 2 , 3 , 5 , 7 , 11 , 13 ,因为这样过后绝大部分的自然数就已经筛没了。
Pollard_Rho:
  1. 先来考虑一个比较运气的东西,就是用随机数来试着看能不能整除。
  2. 显然上面的做法效率太低,我们来看一下生日悖论给我们的启发:

生日悖论,指如果一个房间里有23个或23个以上的人,那么至少有两个人的生日相同的概率要大于50%。这就意味着在一个典型的标准小学班级(30人)中,存在两人生日相同的可能性更高。对于60或者更多的人,这种概率要大于99%。

所以上面到底讲了什么呢?总结来说就是在一定的范围内随机选择一些数,当选的数的个数 n (n是范围的大小)时,有相当大的概率会出现相同的数。考虑怎么将这个神奇的发现运用到分解约数上去,每一个随机数在模因数 p 下可以看做是一个小于 p 的数(虽然这个 p 我们不知道是多少,但是它仍旧在那里),两个模 p 相同的数作差的话肯定就是 p 的倍数了,所以我们的目标现在就变成了找到两个模 p 意义下相同的数来做差,然后用 gcd 来判断是否成立。这样根据生日悖论,我们只要用大量的随机数来两两相减就一定能试出一个约数。这就是 P o l l a r d R h o 算法的理论基础。

但是说了这么多 P o l l a r d R h o 的这个 ρ 还没有体现出来,同时我们发现如果两两作差的话,虽然随机数的量 n 1 4 ,但是运算量还是高达 n 在信息学竞赛这个 1 e 18 的范围下显然有问题,这个时候就要敢于做梦,想象一下如果我们在把这 n 1 4 个随机数造出来了之后就可以找到相同的数那该有多好,那么就要用到一个函数 f ( x ) = ( x 2 + c ) mod n (这个 n 是待分解的数)我们可以用这个函数不断地制造伪随机数,观察一下这个函数的特性,即后一个随机数只和前一个随机数有关,再进一步地推导,根据整数同余的相关性质又可以得出来后一个数模 p 的值只和前一个数模 p 的值有关,虽然我们不知道 p 是多少,但是可以确定的是 n p 的倍数,所以在这样不断制造随机数的过程中,在模 n 意义下还没有形成环的时候,模 p 意义下早已形成了环,并且环的大小在期望意义下 p ,这时只要在模 n 意义下一个一个地试就好了,至于判断模 n 意义下的环并且跳出,网上的资料太多,有Floyd和Brent两种,这里就不再赘述。
注意溢出和常数的影响。

/*============================================
 * Author : ylsoi
 * Problem : Pollard Rho
 * Algorithm : Pollard Rho and Miller Rabin
 * Time : 2018.7.15
 * ==========================================*/
#include<bits/stdc++.h>

#define REP(i,a,b) for(register int i=a;i<=b;++i)
typedef long long ll;

using namespace std;

void File(){
    freopen("Pollard_Rho.in","r",stdin);
    freopen("Pollard_Rho.out","w",stdout);
}

ll qmul(ll x,ll b,ll mod){
    ll tmp=(x*b-(ll)((long double)x/mod*b+1e-8)*mod);
    return tmp<0?tmp+mod:tmp;
    /*ll ret=0,base=x%mod;
    while(b){
        if(b&1)ret=(ret+base)%mod;
        base=(base+base)%mod;
        b>>=1;
    }
    return ret;*/
}

ll qpow(ll x,ll b,ll mod){
    ll ret=1,base=x%mod;
    while(b){
        if(b&1)ret=qmul(ret,base,mod);
        base=qmul(base,base,mod);
        b>>=1;
    }
    return ret;
}

bool Miller_Rabin(ll p){
    if(p==1 || p==2 || p==3 || p==5 || p==7 || p==11 || p==13)return true;
    if(p%2==0 || p%3==0 || p%5==0 || p%7==0 || p%11==0 || p%13==0)return false;
    ll t=p-1,cnt=0;
    while(t%2==0)t>>=1,++cnt;
    REP(i,1,10){
        ll a=rand()%(p-1)+1,x=qpow(a,t,p);
        REP(j,1,cnt){
            ll y=qmul(x,x,p);
            if(y==1 && x!=1 && x!=p-1)return false;
            x=y;
        }
        if(x!=1)return false;
    }
    return true;
}

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

ll Pollard_Rho(ll x){
    ll c=rand()%(x-1)+1,a=rand()%(x-1)+1,b=a,g;
    for(int k=1,step=1;;++step){
        b=(qmul(b,b,x)+c)%x;
        g=gcd(abs(a-b),x);
        if(g!=1 && g!=x)return g;
        if(a==b)return x;
        if(k==step){
            a=b;
            k<<=1;
        }
    }
    /*ll c=rand(),a=rand(),b=(qmul(a,a,x)+c)%x,g;
    while(1){
        g=gcd(abs(a-b),x);
        if(g!=1 && g!=x)return g;
        a=(qmul(a,a,x)+c)%x;
        b=(qmul(b,b,x)+c)%x;
        b=(qmul(b,b,x)+c)%x;
        if(a==b)return x;
    }*/
}

int n;
ll ans;

void findfac(ll x){
    if(Miller_Rabin(x)){
        ans=max(x,ans);
        return;
    }
    ll g=Pollard_Rho(x);
    while(g==x)g=Pollard_Rho(x);
    findfac(x/g);
    findfac(g);
}

int main(){
    File();
    srand((unsigned)time(NULL));
    scanf("%d",&n);
    REP(i,1,n){
        ll x;
        scanf("%lld",&x);
        if(Miller_Rabin(x))puts("Prime");
        else{
            ans=0;
            findfac(x);
            printf("%lld\n",ans);
        }
    }
    //cerr<<clock()<<endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/ylsoi/article/details/81057612
今日推荐