• 欧拉函数专题


    一、欧拉函数概念

    \(ϕ(n)\)表示\(n\)的欧拉函数值,代表了从\(1\)\(n\)\(n\)互质的数的个数,例如\(ϕ(8)=4\) 因为\(1,3,5,7\)均和\(8\)互质。特殊的\(ϕ(1)=1\)

    二、欧拉函数公式

    根据算法基本定理,数字\(n\)必然可以描述为如下数学公式:

    $\large n={p_1}^{\alpha_1} * {p_2}^{\alpha_2} * {p_3}^{\alpha_3} * ... * {p_k}^{\alpha_k}$
    欧拉函数的公式:
    $\large ϕ(n)=n*(1-\frac{1}{p_1})*(1-\frac{1}{p_2})*(1-\frac{1}{p_3})*(1-\frac{1}{p_4})*……*(1-\frac{1}{p_k})$

    其中\(p_1, p_2……p_k\)\(n\)的所有质因数,\(n\)是不为\(0\)的整数。\(φ(1)=1\)(唯一和\(1\)互质的数就是\(1\)本身)。
    注意:欧拉函数与幂指数$ \alpha_i $无关,只与质数因子有关。

    三、求单个数字的欧拉函数

    #include <bits/stdc++.h>
    
    using namespace std;
    
    /**
     * 功能:欧拉函数
     * @param x
     * @return
     */
    int phi(int x) {
        int res = x;
        for (int i = 2; i <= x / i; i++)
            if (x % i == 0) {
                res = res / i * (i - 1);
                while (x % i == 0) x /= i;
            }
        if (x > 1) res = res / x * (x - 1);
        return res;
    }
    
    int main() {
        cout << phi(2) << endl;
        cout << phi(8) << endl;
        return 0;
    }
    

    四、筛法求欧拉函数

    1、依托于线性筛法,可以顺带求出欧拉函数值。如果对数论了解更多,会知道线性筛法还可以求出很多其它的东西。

    2、\(phi[1]=1\)

    3、对区间内每个数进行分情况讨论:

    • 如果这个数是质数,那么质数\(i\)的欧拉函数值是\(phi[i]=i-1\)
      比如\(7\),那么\(1-6\)当中有多少个数与\(7\)互质呢?显然\(6\)个都是嘛。

    • 数字\(i\)在被\(primes[j]\)尝试筛的过程中:(这里设 \(p_j=primes[j]\)以简便下面的书写)
      如果\(i\ \% \ p_j = 0\), 那么 \(phi[i * p_j] = phi[i] * p_j\)
      证明:

      \(\because\) \(i={p_1}^{\alpha_1} * {p_2}^{\alpha_2} * {p_3}^{\alpha_3} * ... *{p_j}^{\alpha_j}\) 【算术基本定理】
      $phi[i]= i * (1- \frac{1}{p_1}) * (1- \frac{1}{p_2}) * (1- \frac{1}{p_3}) * ... * (1- \frac{1}{p_j}) $ 【欧拉公式】

      \(phi[p_j*i]\)分解质数因数的结果,只比\(phi[i]\)多分解了一个\(p_j\),而 \(i \ \% \ p_j = 0\) 说明\(phi[i]\)中存在\(p_j\)因子,只不过指数增加了\(1\)个。

      \(\therefore\) \(p_j * i ={p_1}^{\alpha_1} * {p_2}^{\alpha_2} * {p_3}^{\alpha_3} * ... *{p_j}^{\alpha_j+1}\)

      \(\therefore\) $phi[p_j * i]= phi[i] * p_j $ 【证毕】

    • 如果\(i\ \% \ p_j > 0\), 那么 \(phi[i * p_j] = phi[i] * (p_j-1)\)
      证明:
      \(\because\) \(i={p_1}^{\alpha_1} * {p_2}^{\alpha_2} * {p_3}^{\alpha_3} * ... *{p_k}^{\alpha_k}\)

      $phi[i]= i * (1- \frac{1}{p_1}) * (1- \frac{1}{p_2}) * (1- \frac{1}{p_3}) * ... * (1- \frac{1}{p_k}) $

      \(phi[p_j*i]\)分解质数因数的结果,只是比\(phi[i]\)多分解了一个\(p_j\),而 \(i \ \% \ p_j>0\) 说明\(phi[i]\)中不存在\(p_j\)这个因子,需要写上去。

      \(p_j * i ={p_1}^{\alpha_1} * {p_2}^{\alpha_2} * {p_3}^{\alpha_3} * ... * {p_k}^{\alpha_k} * {p_j}^{1}\)

      \(\therefore\) $phi[p_j * i]= p_j * i * (1- \frac{1}{p_1}) * (1- \frac{1}{p_2}) * (1- \frac{1}{p_3}) * ... * (1- \frac{1}{p_k}) * (1- \frac{1}{p_j}) $

      \(\therefore\) $phi[p_j * i]= p_j * phi[i] * (1 - \frac{1}{p_j}) = phi[i] * ( p_j -1) $ 【证毕】

    #include <bits/stdc++.h>
    
    using namespace std;
    
    //筛法求欧拉函数
    typedef long long LL;
    
    const int N = 1000010;
    int primes[N];  //保存的是每一个质数
    int size;       //cnt代表质数下标,就是到第几个了
    int phi[N];     //欧拉函数值,一般叫Φ,函数名不能是希腊字母,所以转为phi
    bool st[N];     //代表是不是已经被筛掉了
    LL res;         //结果
    
    void get_eulers(int n) {
        //1的欧拉函数值
        phi[1] = 1;
        //如果当前i没有被筛过,说明当前i是一个质数
        for (int i = 2; i <= n; i++) {
            if (!st[i]) {
                //增加一个质数
                primes[size++] = i;
                phi[i] = i - 1;
            }
            for (int j = 0; primes[j] <= n / i; j++) {
                int t = primes[j] * i;
                st[t] = true;
                if (i % primes[j] == 0) {
                    phi[t] = phi[i] * primes[j];
                    break;
                } else
                    phi[t] = phi[i] * (primes[j] - 1);
            }
        }
    }
    
    int main() {
        //优化读入
        ios::sync_with_stdio(false);
        int n;
        cin >> n;
    
        //线性筛质数的办法,在过程中求得欧拉函数
        get_eulers(n);
    
        for (int i = 1; i <= n; i++) res += phi[i];
        //输出结果
        cout << res << endl;
    
        return 0;
    }
    

    五、欧拉函数的证明

    欧拉函数的证明是使用的融斥原理,从定义出发:

    (1)对数字$N$进行质因数分解: $N={p_1}^{\alpha_1} * {p_2}^{\alpha_2} * {p_3}^{\alpha_3} * ... * {p_k}^{\alpha_k}$

    (2)把\(p_1\)的倍数从\(N\)中减去,因为这些数都肯定不是与\(N\)互质的,有约数\(p_1\)嘛。那需要减去多少个呢?
    \(\frac{N}{p_1}\)个.

    (3) 把\(p_2,p_3,...,p_k\)的倍数都减去吧,分别减去\(\frac{N}{p_2}\),\(\frac{N}{p_3}\),...,\(\frac{N}{p_k}\)个。

    (4) 这么干是减多了的,比如某个数,是\(p_2\)的倍数,也是\(p_3\)的倍数,就减了两回,还需要再加回来\(p_i * p_j\)的倍数,就是 + \(\frac{N}{p_1 * p_2}\)+ \(\frac{N}{p_1 * p_3}\)+ \(\frac{N}{p_1 * p_k}\)+ ....

    (5)将公式\(\phi(N)=N * (1-\frac{1}{p_1}) * (1-\frac{1}{p_2}) * (1-\frac{1}{p_3}) * ... * (1-\frac{1}{p_k})\)展开,发现就是上面的东东了,证明完毕。

  • 相关阅读:
    深度解析VC中的消息传递机制(上)
    DLL的远程注入技术
    一些游戏编程的书[转]
    [转]小小C的C++之歌
    Windows Server 2008无法使用arp命令添加静态MAC绑定
    如何调用未公开的API函数[转]
    IOCP中的socket错误和资源释放处理方法
    TinyXML应用例子
    微软C/C++ 编译器选项参考
    [摘录]这几本游戏编程书籍你看过吗?
  • 原文地址:https://www.cnblogs.com/littlehb/p/15213612.html
Copyright © 2020-2023  润新知