1. 程式人生 > 其它 ><演算法筆記02>尤拉函式

<演算法筆記02>尤拉函式

演算法介紹

在數論,對正整數n,尤拉函式是小於n的正整數中與n互質的數的數目

\(\phi(n)\)的值為是小於等於n的正整數中與n互質的數的數目

公式

求解尤拉函式公式為

假設\(n\)\(t\)個質因子,有如下公式

  • 公式:

    \(\phi(n)=n*(1-\frac{1}{p_1})*(1-\frac{1}{p_2})*...*(1-\frac{1}{p_t})\)

  • 證明:

    根據容斥定理,假設\(n\)被質因子\(p1,p2\)整除,那麼有\(\frac{n}{p1}\)個數與\(n\)不互質,\(\frac{n}{p_2}\)個數與\(n\)不互質,但是\(\frac{n}{p_1*p_2}\)

    被多減去了一次因此要加回來..所以合併一下就是\(\phi(n)=n*(1-\frac{1}{p_1})*(1-\frac{1}{p_2})\)如果有多個質因子也一樣根據容斥的思想計算,一樣可以得到結果。

性質

  1. \(gcd(a,b)=1\)時,\(\phi(a)*\phi(b)=\phi(ab)\)
  2. \(a|b\)(\(a\)整除\(b\))時,\(\phi(ab)=\phi(a)*b\)
  3. \(p\)是質數,\(\phi(p^n)=p^{n-1}*(p-1)\)

性質證明

  1. \(gcd(a,b)=1\)時,\(\phi(a)*\phi(b)=\phi(ab)\)

    根據公式,\(a,b\)

    如果互質,那麼說明沒有公共質因子,\(\phi(ab)\)的展開就是\(\phi(a)*\phi(b)\)

  2. \(a|b\)(\(a\)整除\(b\))時,\(\phi(ab)=\phi(a)*b\)

    根據公式,如果\(a|b\),那麼說明\(b\)擁有的質因子\(a\)一定有,\(\phi(ab)\)展開,b對質因子沒貢獻作用,所以等價於直接乘\(b\)

  3. \(p\)是質數,\(\phi(p^n)=p^{n-1}*(p-1)\),質數的質因子是自己本身,所以直接帶公式即可....

常用板子

  1. 求解\(\phi(n)\)

    思路:試除法求質因子,求的過程中計算即可,時間複雜度\(O(\sqrt{n})\)

    int n;
    cin>>n;
    ll res=n;
    for(int i=2;i<=n/i;i++){
        if(n%i==0)res=res/i*(i-1);
        while(n%i==0)n/=i;
    }
    if(n>1)res=res/n*(n-1);
    cout<<res<<endl;
    
  2. 求解\(\sum_{i=1}^n\phi(i)\)

    思路:可以用埃氏篩法或線性篩法,要運用到上面推導的三個性質。

  • 埃氏篩法
/*
埃氏篩法的過程中,我們對質數進行翻倍,那麼每個數字會被自己所有的質因子篩一遍,我們根據這個性質來“順便”求解尤拉函式
*/
#include <iostream>
const int N=1e6+5;
int phi[N];
int main()
{  
    int n;
    cin>>n;
    long long  res=1;
    for(int i=1;i<=n;i++)phi[i]=i;
    for(int i=2;i<=n;i++){
        if(phi[i]==i){
            for(int j=i;j<=n;j+=i){
                phi[j]=phi[j]/i*(i-1);
            }
        }
        res+=phi[i];
    }
    cout<<res;
}
  • 線性篩法
/*
利用線性篩法的特點:每個數字有且僅被最小質因子篩一遍
*/
#include <iostream>
const int N=1e6+5;
typedef long long ll;
using namespace std;
int phi[N]={0,1};
int p[N],cnt;
bool book[N];
int main()
{  
    int n;
    cin>>n;
    ll res=1;
    for(int i=2;i<=n;i++){
        if(phi[i]==0)p[++cnt]=i,phi[i]=i-1;//沒被篩到過,表明是質數
        for(int j=1;p[j]<=n/i;j++){
            if(i%p[j]==0){
                phi[i*p[j]]=phi[i]*p[j];//性質2  
                break;
            }
            phi[i*p[j]]=phi[i]*phi[p[j]];//性質1  phi[p[j]]在之前以及被計算過
        }
        res+=phi[i];
    }
    cout<<res;
}