Pollard-Rho方法分解整數因子

對於大整數而言,要找出其因子是比較困難的,事實上仍然沒有很好的解決方法。不過在ACM裏面有幾種“標準的”方法可以使用,例如Pollard-Rho方法。
假設題目是求n 的因子,Pollard-Rho方法的基本思想是任意給定整數yx ,求gcd(yx,n) 。注意,所有運算都是在模n 的條件下進行的。如果gcd1gcdn ,則gcd 肯定是n 的一個因子。無論有沒有找到因子,均選取另外一對數yx 繼續。注意到在模n 的意義下,數對是有限的。可以期待這個過程能夠輸出將n分解的所需的足夠的因子。
一般做法是生成一個數列{xn}

xi+1=(x2ic)%n

c 取一個隨機數,但不要取值0或者2。y 也無需另外取值,直接取序列{x1,x2,x4,x8...} 即可。
注意到如果找到pn 的因子,那麼n/p 也是n 的因子。且pn/p 各自的因子也一定是n 的因子,而且pn/p 一定包含了n 的所有可能的因子。因此可以進行遞歸調用。

POJ1811要求判斷給定數是否爲質數,如果是合數則輸出最小的質因子。先用Miller_Rabin方法判質數,然後對合數遞歸分解因子求出最小即可。

#include <cstdio>
#include <algorithm>
using namespace std;

typedef long long llt;

int const Repeat = 10;

//利用二進制計算a*b%mod
llt multiMod(llt a,llt b,llt mod){
    llt ret = 0LL;
    a %= mod;
    while( b ){
        if ( b & 1LL ) ret = ( ret + a ) % mod, --b;
        b >>= 1LL;
        a = ( a + a ) % mod;
    }
    return ret;
}

//計算a^b%mod
llt powerMod(llt a,llt b,llt mod){
    llt ret = 1LL;
    a %= mod;
    while( b ){
        if ( b & 1LL ) ret = multiMod(ret,a,mod),--b;
        b >>= 1LL;
        a = multiMod(a,a,mod);
    }
    return ret;
}

//Miller-Rabin測試,測試n是否爲素數
bool Miller_Rabin(llt n,int repeat){
    if ( 2LL == n || 3LL == n ) return true;
    if ( !( n & 1LL ) ) return false;

    //將n分解爲2^s*d
    llt d = n - 1LL;
    int s = 0;
    while( !( d & 1LL ) ) ++s, d>>=1LL;

    //srand((unsigned)time(0));
    for(int i=0;i<repeat;++i){//重複repeat次
        llt a = rand() % ( n - 3 ) + 2;//取一個隨機數,[2,n-1)
        llt x = powerMod(a,d,n);
        llt y = 0LL;
        for(int j=0;j<s;++j){
            y = multiMod(x,x,n);
            if ( 1LL == y && 1LL != x && n-1LL != x ) return false;
            x = y;
        }
        if ( 1LL != y ) return false;
    }
    return true;
}

llt Fac[100];//質因數分解結果(剛返回時是無序的)
int FCnt;//質因數的個數。數組小標從0開始

llt gcd(llt a,llt b){
    if (0L == a || 0L == b) return 1;
    if ( a < 0 ) a = -a;
    if ( b < 0 ) b = -b;
    while(b){
        llt t = a % b;
        a = b;
        b = t;
    }
    return a;
}
llt Pollard_Rho(llt n,llt c){
    llt i = 1, k = 2;
    llt x = rand() % n;
    llt y = x;
    while(1){
        ++i;
        x = ( multiMod(x,x,n) + c ) % n;
        llt d = gcd(y-x,n);
        if (d!=1LL && d!=n) return d;
        if (y == x) return n;
        if ( i == k ) y = x, k <<= 1;
    }
}

void find(llt n){
    if ( 4LL == n ){
        Fac[0] = Fac[1] = 2LL;
        FCnt = 2;
        return;
    }
    if ( Miller_Rabin(n,Repeat) ){
        Fac[FCnt++] = n;
        return;
    }

    llt p;
    while( ( p = Pollard_Rho(n,rand()%(n-3)+3) ) == n );

    find(p);
    find(n/p);
}

int main(){
    int kase;
    scanf("%d",&kase);
    while(kase--){
        llt n;
        scanf("%I64d",&n);

        FCnt=0;
        find(n);


        if(Fac[0] == n) printf("Prime\n");
        else printf("%I64d\n",*min_element(Fac,Fac+FCnt));
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章