1. 程式人生 > >Pollard Rho 大數分解

Pollard Rho 大數分解

這裡的a和b就是用函式f=x^2+1生成的,b=a^2+1.

實現程式碼:


#include <iostream>
#include <cstdio>
#include <stdlib.h>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int N=6e5;
LL fac[N],num[N];
int cnt;

LL gcd(LL a,LL b){
    return b==0?a:gcd(b,a%b);
}
LL multi(LL a,LL b,LL m){
    LL ans=0;
    while(b){
        if(b&1) ans=(ans+a)%m;
        a=(a+a)%m;
        b>>=1;
    }
    return ans;
}
LL quick_mod(LL a,LL p,LL m){
    LL ans=1;
    while(p){
        if(p&1) ans=multi(ans,a,m);
        a=multi(a,a,m);
        p>>=1;
    }
    return ans;
}
LL Pollard_rho(LL n,LL c){
    LL x,y,k=2,i=1;
    x=rand()%(n-1)+1;
    y=x;
    while(1>0){
        i++;
        x=(multi(x,x,n)+c)%n;
        LL d=gcd((y-x+n)%n,n);
        if(1<d&&d<n) return d;
        if(y==x)  return n;   // 出現迴圈
        if(i==k) {
            y=x;
            k<<=1;  //x 比y多跑一圈
        }
    }
}
bool Miller_rabin(LL p){
    if(p==2) return 1;
    if(p<2 || (p&1)==0) return 0;
    LL m=p-1;
    int sum=0;
    while((m&1)==0){
        m>>=1;
        sum++;
    }
    for(int i=0;i<10;i++){
        LL a=rand()%(p-1)+1;
        LL x=quick_mod(a,m,p);
        LL g=0;
        for(int j=0;j<sum;j++){
            g=multi(x,x,p);
            if(g==1&&x!=1&&x!=p-1)  return 0;
            x=g;
        }
        if(g!=1) return 0;
    }
    return 1;
}

void find(LL n,LL c){
    if(n==1) return ;
    if(Miller_rabin(n)){
        fac[cnt++]=n;
        return ;
    }
    LL p=n;
    while(p>=n) p=Pollard_rho(p,c--);  //迴圈避免沒找到
    find(p,c);
    find(n/p,c);
}
int main()
{
    //freopen("cin.txt","r",stdin);
    LL x;
    while(cin>>x){
        cnt=0;
        find(x,120);
        sort(fac,fac+cnt);
        memset(num,0,sizeof(num));
        int k=0;
        for(int i=0;i<cnt;i++){
            if(i==0){
                num[k]++;
                continue;
            }

            if(fac[i]==fac[i-1]){
                num[k]++;
            }
            else {
                k++;
                fac[k]=fac[i];
                num[k]++;
            }
        }
        for(int i=0;i<k;i++){
            printf("%lld^%lld*",fac[i],num[i]);
        }
        printf("%lld^%lld\n",fac[k],num[k]);
    }
    return 0;
}
/*
自制資料,經mathematica驗證,OK。

In[14]:= FactorInteger[232290989000]

Out[14]= {{2, 3}, {5, 3}, {7, 1}, {61, 1}, {544007, 1}}

In[15]:= FactorInteger[23432590000448]

Out[15]= {{2, 6}, {466201, 1}, {785357, 1}}

In[16]:= FactorInteger[43253245288004]

Out[16]= {{2, 2}, {1493, 1}, {7242673357, 1}}

In[17]:= FactorInteger[200998882133322200]

Out[17]= {{2, 3}, {5, 2}, {29, 1}, {967, 1}, {35837621177, 1}}

In[18]:= FactorInteger[8900446284609340]

Out[18]= {{2, 2}, {5, 1}, {139, 1}, {881, 1}, {3634051513, 1}}

In[19]:= FactorInteger[900046234690488422]

Out[19]= {{2, 1}, {14081, 1}, {31959599271731, 1}}

In[20]:= FactorInteger[4460806408]

Out[20]= {{2, 3}, {3041, 1}, {183361, 1}}

input:
232290989000

23432590000448

43253245288004

200998882133322200

8900446284609340

900046234690488422

4460806408

output:
2^3*5^3*7^1*61^1*544007^1
2^6*466201^1*785357^1
2^2*1493^1*7242673357^1
2^3*5^2*29^1*967^1*35837621177^1
2^2*5^1*139^1*881^1*3634051513^1
2^1*14081^1*31959599271731^1
2^3*3041^1*183361^1

Process returned 0 (0x0)   execution time : 0.305 s
Press any key to continue.
*/ 

POJ 1811 Prime Test 大意:判斷一個數字是否是素數,如果不是輸出最小的素因子。
#include <iostream>
#include <cstdio>
#include <stdlib.h>
using namespace std;
typedef long long LL;
LL min_fac;
LL gcd(LL a,LL b){
    return b==0?a:gcd(b,a%b);
}
LL multi(LL a,LL p,LL m){
    LL ans=0;
    while(p){
        if(p&1) ans=(ans+a)%m;
        a=(a+a)%m;
        p>>=1;
    }
    return ans;
}
LL quick_mod(LL a,LL p,LL m){
    LL ans=1;
    while(p){
        if(p&1) ans=multi(ans,a,m);
        a=multi(a,a,m);
        p>>=1;
    }
    return ans;
}
bool Miller_Rabin(LL p){
     if(p==2) return 1;
     if(p<2 || (p&1)==0) return 0;
     int sum=0;
     LL m=p-1;
     while((m&1)==0){
        m>>=1;
        sum++;
     }
     for(int i=0;i<10;i++){
        LL a=rand()%(p-1)+1;
        LL x=quick_mod(a,m,p);
        LL g=0;
        for(int j=0;j<sum;j++){
            g=multi(x,x,p);
            if(g==1&&x!=1&&x!=p-1) return 0;
            x=g;
        }
        if(g!=1) return 0;
     }
     return 1;
}
LL Pollard_rho(LL n,LL c){
    LL i=1,k=2;
    LL x=rand()%(n-1)+1;
    LL y=x;
    while(1>0){
        i++;
        x=(multi(x,x,n)+c)%n;
        LL d=gcd((y-x+n)%n,n);
        if(d<n&&d>1) return d;
        if(x==y) return n;
        if(i==k){
            y=x;
            k<<=1;
        }
    }
}
void find(LL x,LL c){
    if(x==1) return ;
    if(Miller_Rabin(x)){
        min_fac=min_fac<x?min_fac:x;
        return ;
    }
    LL p=x;
    while(p>=x) p=Pollard_rho(p,c--);
    find(p,c);
    find(x/p,c);
}
int main()
{
    //freopen("cin.txt","r",stdin);
    int t;
    cin>>t;
    while(t--){
        LL x;
        scanf("%lld",&x);
        min_fac=x;
        if(Miller_Rabin(x)) {
            puts("Prime");
            continue;
        }
        find(x,120);
        printf("%lld\n",min_fac);
    }
    return 0;
}

POJ 2429 GCD & LCM Inverse 大意: 給出最大公倍數和最小公約數 求出原來的兩個數字,升序輸出,有多個的話輸出和最小的一個。 分析: gcd(a,b)=G, lcm(a,b)=L 那麼L/G=a/G * b/G 如果a+b最小,那麼a/G + b/G也是最小的。a/G * b/G和a/G + b/G有什麼聯絡?在答案一定的情況下,當a/G 和 b/G的差越小,a/G + b/G越大。(周長一定的情況下,圓是面積最大的二維圖形) 所以,我們對r=L/G因子分解。找到因子積最大值(<=sqrt(r)) --》 ans 找到這樣一個值也需要注意,不是直接排個序,相乘比較就行。比如: 
假設一個值分解成:2 3 4 5  --> 120 LL limit=(LL) sqrt(120)=10 ans=2*3嗎? 顯然不是,2*5才是答案

#include <iostream>
#include <cstdio>
#include <stdlib.h>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long LL;
const int N=5e5+10;
LL fac[N],ff[N],cnt;

LL gcd(LL a,LL b){
    return b==0?a:gcd(b,a%b);
}
LL multi(LL a,LL p,LL m){
    LL ans=0;
    while(p){
        if(p&1) ans=(ans+a)%m;
        a=(a+a)%m;
        p>>=1;
    }
    return ans;
}
LL quick_mod(LL a,LL p,LL m){
    LL ans=1;
    while(p){
        if(p&1) ans=multi(ans,a,m);
        a=multi(a,a,m);
        p>>=1;
    }
    return ans;
}
bool Miller_Rabin(LL p){
     if(p==2) return 1;
     if(p<2 || (p&1)==0) return 0;
     int sum=0;
     LL m=p-1;
     while((m&1)==0){
        m>>=1;
        sum++;
     }
     for(int i=0;i<10;i++){
        LL a=rand()%(p-1)+1;
        LL x=quick_mod(a,m,p);
        LL g=0;
        for(int j=0;j<sum;j++){
            g=multi(x,x,p);
            if(g==1&&x!=1&&x!=p-1) return 0;
            x=g;
        }
        if(g!=1) return 0;
     }
     return 1;
}
LL Pollard_rho(LL n,LL c){
    LL i=1,k=2;
    LL x=rand()%(n-1)+1;
    LL y=x;
    while(1>0){
        i++;
        x=(multi(x,x,n)+c)%n;
        LL d=gcd((y-x+n)%n,n);
        if(d<n&&d>1) return d;
        if(x==y) return n;
        if(i==k){
            y=x;
            k<<=1;
        }
    }
}
void find(LL x,LL c){
    if(x==1) return ;
    if(Miller_Rabin(x)){
        fac[cnt++]=x;
        return ;
    }
    LL p=x;
    while(p>=x) p=Pollard_rho(p,c--);
    find(p,c);
    find(x/p,c);
}
int main()
{
    LL G,L;
    while(cin>>G>>L){
        if(G==L){
            printf("%lld %lld\n",G,L);
            continue;
        }
        LL r=L/G;
        cnt=0;
        find(r,120);
        sort(fac,fac+cnt);
        int top=0;
        for(int i=0;i<cnt;i++){
            if(i==0){
                ff[top]=fac[i];
                continue;
            }
            if(fac[i]==fac[i-1]){
                ff[top]=ff[top]*fac[i];
            }
            else {
                top++;
                ff[top]=fac[i];
            }
        }
        top++;
        LL ans=1,temp=1;
        LL limit=(LL)sqrt(r*1.0);
        for(int i=1;i<(1<<top);i++){
            temp=1;
            for(int j=0;j<top;j++){
                if(i&(1<<j)) temp*=ff[j];
            }
            if(temp<=limit) {
                ans=ans>temp?ans:temp;
            }
        }
        printf("%lld %lld\n",ans*G,r/ans*G);
    }
    return 0;
}