Pollard Rho 大數分解
阿新 • • 發佈:2019-01-04
這裡的a和b就是用函式f=x^2+1生成的,b=a^2+1.
實現程式碼:
POJ 1811 Prime Test 大意:判斷一個數字是否是素數,如果不是輸出最小的素因子。
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 <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 找到這樣一個值也需要注意,不是直接排個序,相乘比較就行。比如:
#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;
}