Pollard-Rho算法求大數質因子

/*
 * 大整數分解到現在都是世界級的難題,但卻是一個重要的研究方向,大整數在公共密鑰的研究上有着重要的作用
 * Pollard Rho算法的原理就是通過某種方法得到兩個整數a和b.而待分解的大整數爲n,計算p=gcd(abs(a-b),n),直到p不爲1或者a,b出現循環爲止.
 * 然後再判斷是否爲n,如果p==n||p==1,那麼返回n時一個質數
 * 否則p就是n的一個因子
 * 那麼我們又可以遞歸地計算Pollard(p)和Pollard(n/p).
 * 最後就可以推出n的所有質因子
 *
 * 具體操作中我們常常使用函數 x[i+1]=(x[i]*x[i]+c)%n 來逐步迭代計算a和b的值,通常c取1,即b=a*a+1,在下一次計算中,將b的值賦給a,再次使用上式來計算新的b的值,當a,b出現循環時即可退出判斷.(初值自己確定)
 * 但是這樣的話判斷循環比較麻煩,這裏給出Floyd(沒錯又是他)發明的一個聰明而又有趣的算法:
 *     假設我們在一個很長很長的圓形軌道上面行走,如何知道自己已經走了一圈了呢?
 *     可以讓兩個人A和B按照 vb = va<<1 從同一起點開始向前走,當B第一次趕上A時,我們就知道B已經走了兩圈
 *     所以我們可以把x當作B,把y當作A,然後進行循環測試
 * 
 * 對於Pollard Rho算法,它可以在O(sqrt(p))的時間複雜度內找到n的一個小因子p,可見效率還是可以的
 * 但是對於一個因子很少或者因子值很大的大整數n來說,這個算法的複雜度依然不是很好
 */
//以下給出Pollard Rho和Miller-Rabin素數測試配合使用的整數分解算法
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
typedef long long ll;
const int counts = 10,N = 5001;

ll tot,cnt,fac[N],num[N];

ll gcd(ll a,ll b)
{return b?gcd(b,a%b):a;}

ll qpow(ll a,ll x,ll p)
{
    ll ret=1;
    for(;x;x>>=1,a=a*a%p)
        if(x&1)
            ret=ret*a%p;
    return ret;
}

ll multi(ll a,ll b,ll p)
{
    ll ans=0;
    a%=p;
    for(;b;b>>=1,a=(a<<1)%p)
        if(b&1)
            ans=(ans+a)%p;
    return ans;
}

bool Miller_Rabin(ll n)
{
    if(n==2)return true;
    if(n<2 || !(n&1))return false;
    ll m=n-1,a,x,y;int k=0;
    while(!(m&1))++k,m>>=1;
    for(int i=0;i<counts;++i)
    {
        a=rand()%(n-1)+1;
        x=qpow(a,m,n);
        y=0;
        for(int j=0;j<k;++j)
        {
            y=multi(x,x,n);
            if(y==1 && x!=-1 && x!=n-1)return false;
            x=y;
        }
        if(y != -1)return false;
    }
    return true;
}

ll Pollard_Rho(ll n,ll c)
{
    ll i=1,k=2,x=rand()%(n-1)+1,y=x,d;
    while("fighting")
    {
        ++i;
        x=(multi(x,x,n)+c)%n;
        d=gcd((y-x+n),n);
        if(1<d && d<n)return d;
        if(y == x)return n;
        if(i == k)y=x,k<<=1;
    }
}

void find(ll n,int c)
{
    if(n == 1)return ;
    if(Miller_Rabin(n))
    {
        fac[tot++]=n;
        return ;
    }
    ll p=n,k=c;
    while(p>=n)p=Pollard_Rho(p,c--);
    find(p,k);
    find(n/p,k);
}

int main()
{
    ll n;
    while(std::cin>>n)
    {
        tot=0;
        find(n,120);
        std::sort(fac,fac+tot);
        num[0]=1;
        int k=1;
        for(int i=1;i<tot;++i)
        {
            if(fac[i] == fac[i-1])
                ++num[k-1];
            else
            {
                num[k]=1;
                fac[k++]=fac[i];
            }
        }
        cnt=k;
        for(int i=0;i<cnt;++i)
            std::cout<<fac[i]<<"^"<<num[i]<<" ";
        std::cout<<std::endl;
    }
    return 0;
}
View Code

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章