欧拉函数

1 ~ N 中与 N 互质的数互质是公约数只有1的两个整数,叫做互质)的个数被称为欧拉函数,记为ϕ(N)。
若在算数基本定理中,\(N=p_1^{\alpha_1}p_2^{\alpha_2}\cdots{p_m^{\alpha_m}}\),则有*式:

\[ϕ(N) = N\times\frac{p_1−1}{p_1}\times\frac{p_2−1}{p_2}\cdots\frac{p_m−1}{p_m} \]

证明:

  • 分解质因数N

  • 去掉N的质因数\(p_1 \cdots p_m\)所有的倍数,因为\(p_1,p_2\cdots{p_k}\) 一定不与N互质。

    \[\phi(N) = N - \lfloor{\frac{N}{p_1}}\rfloor - \lfloor\frac{N}{p_2}\rfloor \cdots \lfloor\frac{N}{p_k}\rfloor \]

  • 因为pi pj的公倍数会被去掉两次,所以任取i,j pi *pj的倍数都要再加一次。即加上\(\frac{N}{pi \times pj}\)

  • 可见pi pj pk 的公倍数会被减去三次 又被加上三次,所以我们还要减去一次 即减去任取pi pj pk \(\frac{N}{pi \times pj \times pk}\)

  • 所以

    \[\phi(N) = N - \lfloor\frac{N}{p_1}\rfloor - \lfloor\frac{N}{p_2}\rfloor\ - \cdots \lfloor\frac{N}{p_m}\rfloor +\lfloor\frac{N}{p_1 p_2}\rfloor + \cdots + \lfloor\frac{N}{p_i p_j}\rfloor - \lfloor\frac{N}{p_1 p_2 p_3}\rfloor -\cdots - \lfloor\frac{N}{p_i p_j p_k}\rfloor \]

  • 可以发现*式展开即为上式。证明完毕

代码模板:

输入k个数 输出他们对应的欧拉函数

#include <iostream>
using namespace std;
int main(){
    int n ;
    cin >> n;
    int res =  n;
    //分解质因数模板
    for(int i =2;i <= n / i;i ++){//只要枚举到根号n就可以,因为一个数最多只存在一个比根号n大的质因数
        if(n % i == 0) {//这里符合条件的i只可能是质数,因为如果是合数,他肯定被之前枚举到他的一个质因数的时候就被除干净了
            res = res / i * (i - 1);//套用公式
            while(n % i == 0) n /= i;//找到一个质因数就把他除干净
        }
    }
    if(n > 1) res =res / n * (n - 1);//单独处理最后一个大于根号n的质因数
    printf("%d\n",res);
    
    return 0;
}

算法瓶颈主要是在分解质因数,分解质因数的时间复杂度为\(O(\sqrt{n})\),所以上述模板时间复杂度为\(O(\sqrt{n})\)

有的时候我们需要求出1到N中每一个数的欧拉函数,如果每个数都使用上述的算法,那么时间复杂度为\(O(n\sqrt{n})\)比较慢,可以用线性筛法,以\(O(n)\)的时间复杂度求出所有数的欧拉函数

代码:求1---N中所有数的欧拉函数之和

#include<iostream>
using namespace std;
const int N = 1000010;
typedef long long LL;
int phi[N];//记录每个数的欧拉函数
int primes[N];//记录质数
bool st[N];//记录这个数是不是质数
int cnt;//质数的个数

LL get_eular(int n ){//改造线性筛法
    phi[1] = 1;
    LL res = 0;
    for(int i = 2;i <= n;i ++) {
        if(!st[i]) {//当前的数是质数
            primes[cnt ++] = i;
            phi[i] = i - 1; // 质数i与其互质的数就是1 --- i -1,所以就有i -1个互质的数
        }
        for(int j = 0; j < cnt && primes[j] <= n / i;j ++) {
            st[primes[j] * i] = true;
            if(i % primes[j] == 0) {//如果i % primes[j] == 0 说明primes[j]是i的最小质因数,也就是primes[k] * i的最小质因数
                phi[primes[j] * i] = phi[i] * primes[j];
                break;
            }
            phi[primes[j] * i] = phi[i] * (primes[j] - 1) ;
        }
    }
    for(int i = 1;i <= n ;i ++) {
        res += phi[i];
    }
    return res;
}


int main(){
    int n ;
    cin >> n ;
    
    LL res = get_eular(n);
    cout << res << endl;
    return 0;
}

线性筛法的详细描述可以看我之前的博客,现对线性筛法进行如下改造

  • 如果当前枚举到的数是质数那么phi[i] = i - 1;质数i与其互质的数就是1 --- i -1,所以就有i -1个互质的数,即phi[i] = i -1;

  • 如果 i % primes[j] == 0 说明primes[j]是i的最小质因数,也是primes[k] * i的最小质因数(k > j) ,假设i分解质因数为 \(primes[j] ^ {\alpha_1} \times \cdots primes[m]^ {\alpha_m}\)

    \(\phi(i) = i \times (primes[j] - 1) / primes[j] \times \cdots \times(primes[m] - 1)/ primes[m]\)

    又因为x = primes[j] * i的分解质因数结果只比i分解质因数的结果多乘一个primes[j] 所以 \(\phi(x) = primes[j] \times i \times (primes[j] - 1) / primes[j] \times \cdots \times(primes[m] - 1)/ primes[m]\)

    所以\(\phi(x) = \phi(i * primes[j]) = \phi(i) \times primes[j]\)

    所以可以由phi[i] 得到phi[primes[j] * i].即phi[primes[j] * i ] = phi[i] * primes[j];

  • 如果i % primes[j] != 0 说明i中没有primes[j]这个质因数,则x = primes[j] * i 的欧拉函数比i的欧拉函数式子多乘了一个质因数primes[j]的补偿

    即$\phi(x) = \phi(primes[j] * i) = primes[j] \times i \times (primes[k] - 1) / primes[j] \times \cdots \times(primes[m] - 1)/ primes[m] \times (primes[j] - 1) / primes[j] $

    可以看到多了一个\(primes[j] \times (primes[j] - 1 )/ primes[j]\)的补偿,化简得到多了一个\(primes[j] - 1\)的补偿

    所以\(\phi(x) = \phi(primes[j] * i) = \phi(i) \times (primes[j] - 1)\)

    所在i % primes[j] != 0的时候phi[i]也可以推出phi[i * primes[j]] 即phi[i * primes[j]] = phi[i] * (primes[j] - 1);

posted @ 2020-09-17 22:03  驿站Eventually  阅读(168)  评论(0编辑  收藏  举报