蒟蒻最近準備狂補數學啦TAThtml
基於篩素數,能夠同時快速求出歐拉函數。因而蒟蒻準備從這裏入手,整理一下實現的思路。函數
傳統篩素數的作法(埃式篩)是,利用已知的素數,去篩掉含有此質因子的合數,十分巧妙。因爲不是本文的重點,就只貼一下代碼吧spa
#include<cstdio> #include<cmath> #define R register int const int N=100000000,SQ=sqrt(N); bool f[N]; int main(){ R i,j; for(i=2;i<=SQ;++i){ if(f[i])continue; for(j=i<<1;j<N;j+=i)f[j]=1; } /*for(i=2;i<N;++i) if(!f[i])printf("%d\n",i);*/ return 0; }
複雜度不會證,不過較近似於線性(大概是\(O(n\log\log n)\)的樣子)。code
實際上蒟蒻打了個表,N與篩的次數大概有這樣的關係htm
爲何是近似的呢?由於每一個合數會被其多個質因子都篩一遍,因此並非嚴格的。blog
因而咱們要想辦法讓每一個合數只被篩掉一次。如何實現呢?咱們可讓每一個合數都只被其最小質因子篩掉(歐拉篩)。get
與上面相比,這種新的更加優秀的寫法有了較大的變化。代碼以下,可結合註釋理解,也很少討論。數學
#include<cstdio> #include<cmath> #define R register int const int N=100000000,B=N>>1; bool f[N]; int pr[B]; int main(){ R i,j,p=0; for(i=2;i<=B;++i){ if(f[i]) for(j=1;j<=p&&i*pr[j]<N;++j){ f[i*pr[j]]=1; if(!(i%pr[j]))break;//這一句話就是使得每一個合數只被最小質因子篩掉的關鍵 //簡要解釋一下,若是pr[j]|i,那麼i就有一個質因子pr[j] //那麼{i*pr[j+k],k∈N*}的最小質因子就是pr[j]而不是pr[j+k]了 } else{ pr[++p]=i; for(j=1;j<=p&&i*pr[j]<N;++j) f[i*pr[j]]=1;//i是質數,因此能夠省掉上面那個判斷,減少常數 } } /*for(i=2;i<N;++i) if(!f[i])printf("%d\n",i);*/ return 0; }
實際運行\(N=10^8\)比上面那種寫法快一半。flash
對一個正整數\(x\)定義歐拉函數\(\phi(x)\),爲\([1,x]\)中與\(x\)互質的整數個數。io
幾個基本內容(下面定義\(p\)爲質數):
這樣的話,是否是能夠像埃式篩同樣,經過某個質數篩去質因子含這個數的合數的同時,計算出這個合數的歐拉函數值呢?好像是不行的,由於可能兩個數的商的歐拉函數值還沒求出來。
這時候,更好的歐拉篩又派上了用場。枚舉\(x\)再枚舉質數\(p\),這時候\(\phi(x)\)和\(\phi(p)\)確定都求出來啦,那麼\(\phi(x*p)\)固然也就求出來啦。
#include<cstdio> #define R register const int N=1000001; int pr[N],phi[N]; bool f[N]; int main(){ R int n,i,j,k,p=0; phi[1]=1;//內容1 for(i=2;i<N;++i){ if(!f[i])phi[pr[++p]=i]=i-1;//內容2 for(j=1;j<=p&&(k=i*pr[j])<N;++j){ f[k]=1; if(i%pr[j])//內容3 phi[k]=phi[i]*(pr[j]-1); else{ phi[k]=phi[i]*pr[j]; break; } } } return 0; }