<算法笔记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})\)如果有多个质因子也一样根据容斥的思想计算,一样可以得到结果。

  • 举例:

    \(\phi(6)=2\) 因为数字\(1,5\)\(6\)互质

    需要注意\(\phi(1)=1\)

性质

  1. \(gcd(a,b)=1\)时,\(\phi(a)*\phi(b)=\phi(ab)\)
  2. \(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\)时,\(\phi(ab)=a*\phi(b)\)

    根据公式,如果\(a|b\)(\(b\%a=0\)),那么说明\(a\)拥有的质因子\(b\)一定有,\(\phi(ab)\)展开,\(a\)对展开式的质因子那几项没贡献作用,所以相当于直接乘\(a\)

  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;
}
posted @ 2021-09-05 22:05  Fanxuwei  阅读(161)  评论(0编辑  收藏  举报