【基礎數論】歐拉函數

歐拉函數

歐拉函數就是指:對於一個正整數n,小於n且和n互質的正整數(包括1)的個數,記作φ(n) 。

歐拉函數的通式:φ(n)=n*(1-1/p1)(1-1/p2)(1-1/p3)*(1-1/p4)……(1-1/pn),其中p1, p2……pn爲n的所有質因數,n是不爲0的整數。φ(1)=1(唯一和1互質的數就是1本身)。

對於上述的通式一定要牢記在心,因爲這是計算歐拉函數最重要的一步

廢話少說,先上代碼:

int euler(int n)  
{  
    int ans=n;  
    for(int i=2;i*i<=n;i++){  
        if(n%i==0){  
            ans-=ans/i;  // 這一步就是對應歐拉函數的通式
            //這一個語句是爲了保證完全消除我們剛纔得到的那個i因子。確保我們下一個得到的i是n的素因子。
            while(n%i==0){  
                n/=i;  
            }  
        }  
    }  
    //這個語句是爲了保證我們已經除完了n的所有的素因子,有可能還會出現一個我們未除的因子,
    //如果結尾出現n>1 ,說明我們還剩一個素因子木有除。
    if(n>1)ans-=ans/n;  
    return ans;  
}

但是我們一般做的題當然不會這麼簡單啊~~來點稍微難一點點的。。。

如果我們要求的數比較多,如果一個一個求那麼很容易就超時,所以我們自然而然就想到——打表

我們先來一個最樸素的打表

void euler()
{
    p[1]=1;
    for(int i=2;i<=MAXN;i++){
        int n=i;
        p[i]=i;
        for(int j=2;j*j<=n;j++){
            if(n%j==0){
                p[i]=p[i]/j*(j-1);
                while(n%j==0) n=n/j;
            }
        }
        if(n>1) p[i]=p[i]/n*(n-1);
    }
    for(int i=2;i<MAXN;i++)
        p[i]+=p[i-1];
}

這種打表方法並不是很理想。。。。

下面推薦 兩種較快的打表方法:

ps:這種好像稍微快那麼一點點~

void euler()  
{  
    E[1]=1;  
    for(int i=2;i<maxn;i++)  
        E[i]=i;  
    for(int i=2;i<maxn;i++){  
        if(E[i]==i)  
        for(int j=i;j<maxn;j+=i){  
            E[j]=E[j]/i*(i-1);  
        }  
    }  
}

第二種:

void euler()  
{  
    for(int i=2;i<maxn;i++){  
        if(!E[i])  
        for(int j=i;j<maxn;j+=i){  
            if(!E[j])E[j]=j;  
            E[j]=E[j]/i*(i-1);  
        }  
    }  
}

上述打表方法的思想和最初的是差不多的。但是它進行了優化,所以比較快。
我們逐步分析一下:

首先,在這裏我們枚舉的是素因子。因爲素因子比較少,如果枚舉素因子的話肯定會大大優化複雜度?

那麼,我們如何保證我們得到的就一定是個素因子呢?這就是我們if語句的作用,例如在第一種方法中,我們在第二個for 循環中就是把所有數,除以素因子。這樣得到的複雜度一定比枚舉每個數,然後找素因子(最開始那個打表法)這種打表要優化的多。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章