由素數篩法到歐拉函數(歐拉函數,線性篩)

前言

蒟蒻最近準備狂補數學啦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\)爲質數):

  1. \(\phi(1)=1\),這個沒啥好說的,由於原本就定義\(1\)\(1\)互質
  2. \(\phi(p)=p-1\),也是很顯然的,由質數定義得出
  3. 若是\(p|x\),那麼\(\phi(x*p)=\phi(x)*p\),不然\(\phi(x*p)=\phi(x)*(p-1)\)。證實的話百度一下吧,蒟蒻不會qwq

這樣的話,是否是能夠像埃式篩同樣,經過某個質數篩去質因子含這個數的合數的同時,計算出這個合數的歐拉函數值呢?好像是不行的,由於可能兩個數的商的歐拉函數值還沒求出來。

這時候,更好的歐拉篩又派上了用場。枚舉\(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;
}

題目

一道模板題及其Sol

相關文章
相關標籤/搜索