1. 程式人生 > >phi(大數質因數分解尤拉函式)

phi(大數質因數分解尤拉函式)

總結一下此題用的知識。。。
快速加,快速冪,素數判斷(Miller_Rabin),gcd,Pollard_Rho。。。。
這裡就寫一個Pollard_Rho
對於一個大整數n,我們取任意一個數x使得x是n的質因數的機率很小,但如果取兩個數x1以及x2使得它們的差是n的因數的機率就提高了(我也不會證明。。。。),如果取x1以及x2使得gcd(abs(x1−x2),n)>1的概率就更高了。這就是Pollard-Rho演算法的主要思想。

對於滿足gcd(abs(x1−x2),n)>1的x1和x2,gcd(abs(x1−x2),n)就是n的一個因數,只需要判斷它是否為素數,若為素數,則是n的質因數,否則遞迴此過程。

其中判斷素數就使用Miller-Rabin演算法。

那麼我們怎樣不斷取得x1和x2呢?
x1在區間[1,n]中隨機(rand)出來,而x2則由x2=(x1*x1%n+c)%n推算出來,其中c為任意給定值,事實證明,這樣就是比較優的。

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#define ll long long
using namespace std;
ll x,y,s[19999999],num;
ll multi(ll a,ll k,ll p){
    ll ans=0;
    while
(k){ if(k&1ll) ans=(a+ans)%p;a=(a+a)%p;k>>=1; } return ans; } ll qpow(ll a,ll k,ll p){ ll ans=1; while(k){ if(k&1ll) ans=multi(a,ans,p);a=multi(a,a,p);k>>=1; } return ans; } bool Miller_Rabin(ll n){ if(n==2) return 1; ll u=n-1,t=0,s=20
; while(!(u&1ll)) t++,u>>=1; while(s--){ ll a=rand()%(n-1)+1; x=qpow(a,u,n); for(int i=1;i<=t;i++){ y=x,x=multi(x,x,n); if(x==1&&y!=1&&y!=n-1)return 0; }if(x!=1) return 0; } return 1; } ll gcd(ll a,ll b){ return b?gcd(b,a%b):a; } ll Pollard_Rho(ll n,ll c){ ll i=1,k=2,x=rand()%(n-1)+1,y=x; while(i++){ x=(multi(x,x,n)+c)%n; ll p=gcd(y-x,n); if(p>1&&p<n) return p; if(x==y) return n; if(i==k) y=x,k<<=1; } } void find(ll n,ll c){ if(n==1) return ; if(Miller_Rabin(n)) { s[++num]=n;return ; } ll p=n,k=c; while(p==n) p=Pollard_Rho(n,c--); find(p,k);find(n/p,k); } ll N; int main(){ freopen("phi.in","r",stdin); freopen("phi.out","w",stdout); scanf("%lld",&N); find(N,99999); //for(int i=1;i<=num;i++) printf("%d ",s[i]); int t1=unique(s+1,s+num+1)-s-1; for(int i=1;i<=t1;i++){ N*=(s[i]-1);N/=s[i]; } printf("%lld",N); }