跳转至

欧拉函数

欧拉函数定义

\(1 \sim n\) 中与 \(n\) 互质的数的个数称为欧拉函数,记为 \(\phi(n)\)

在算数基本定理中,\(n = p_1^{a_1} \times p_2^{a_2} \times \cdots \times p_n^{a_n}\)

\(\phi(n) = n \times \frac{p_1 - 1}{p_1} \times \frac{p_2 - 1}{p_2} \times \cdots \times \frac{p_n - 1}{p_n}\)


欧拉函数证明

\(1 \sim n\) 内:

  1. \(n = 1\),则 \(\phi(1) = 1\) ,1 与 1 互质。

  2. \(n\) 是质数,则 \(\phi(n) = n - 1\),与质数 \(n\) 互质的数是 \(1 \sim n - 1\)

  3. \(n\) 是质数的某几次方:\(n = p^k\),则在 \(1 \sim n\) 内与 \(n\) 不互质的有:

    \(p、2p、3p、\cdots、p^{k - 1}p\)

    总共有 \(p^{k - 1}\) 个与 \(n\) 不互质的,所以 \(\phi(n) = p^k - p^{k - 1} = p^k \times (1 - \frac{1}{p})\)

  4. \(n\) 是两个互质的数的乘积:\(n = a \times b\),则 \(\phi(n) = \phi(a) \times \phi(b)\),证明请看下一章。

有了上面的基础之后,根据算术基本定理有:

\(n = p_1^{a_1} \times p_2^{a_2} \times \cdots \times p_n^{a_n}\)

并且 \(p_1^{a_1}、p_2^{a_2}、\cdots 、p_n^{a_n}\) 任意一对都互质,所以根据上面的推理第 4 点有:

\(\phi(n) = \phi(p_1^{a_1}) \times \phi(p_2^{a_2}) \times \cdots \phi(p_n^{a_n})\)

再根据上面的推理第 3 点我们对每一项 \(\phi(p_k^{a_k})\) 都能分解成:

\(\phi(p_k^{a_k}) = p_k^{a_k} - p_k^{a_k - 1}\)

所以 \(\phi(n) = (p_1^{a_1} - p_1^{a_1 - 1}) \times (p_2^{a_2} - p_2^{a_2 - 1}) \times \cdots \times (p_n^{a_n} - p_n^{a_n - 1})\)

进一步化简得:\(\phi(n) = p_1^{a_1} \times (1 - \frac{1}{p_1}) \times p_2^{a_2} \times (1 - \frac{1}{p_2}) \times \cdots \times p_n^{a_n} \times (1 - \frac{1}{p_n})\)

而由前面的算术基本定理:\(n = p_1^{a_1} \times p_2^{a_2} \times \cdots \times p_n^{a_n}\)

所以:

\[\begin{aligned} \phi(n) &= p_1^{a_1} \times (1 - \frac{1}{p_1}) \times p_2^{a_2} \times (1 - \frac{1}{p_2}) \times \cdots \times p_n^{a_n} \times (1 - \frac{1}{p_n}) \\ &= p_1^{a_1} \times p_2^{a_3} \times \cdots \times p_n^{a_n} \times (1 - \frac{1}{p_1}) \times (1 - \frac{1}{p_2}) \times \cdots \times (1 - \frac{1}{p_n}) \\ &= n \times (1 - \frac{1}{p_1}) \times (1 - \frac{1}{p_2}) \times \cdots \times (1 - \frac{1}{p_n}) \end{aligned}\]

873. 欧拉函数

给定 \(n\) 个正整数 \(a_i\),叫你求这每一个数的欧拉函数。

代码参考
// 如何将自己的心中所想,用 「文字」、「语言」、「图像」 记录下来

typedef long long ll;
const int N = int (1e7 + 10);
int n;
int a[N], b[N];

void get_ds(int n)
{
    b[0] = 0;
    for (int i = 2; i <= n / i; i ++)
    {
        if (n % i == 0)
        {
            while (n % i == 0) n /= i;
            b[++b[0]] = i;
        }
    }
    if (n > 1) b[++b[0]] = n;
}

void solve(void)
{
    scanf ("%d", &n); for (int i = 1; i <= n; i ++) scanf ("%d", a + i);

    for (int i = 1; i <= n; i ++)
    {
        ll ans = a[i]; get_ds(a[i]);
        for (int i = 1; i <= b[0]; i ++) ans = ans / b[i] * (b[i] - 1);
        printf ("%lld\n", ans);
    }
}

// 并能使读者与我达成共鸣,是我为什么而学习的最本质的追求

874. 筛法求欧拉函数

给定一个整数 \(n\),叫你求 \(1 \sim n\) 中所有数的欧拉函数之和。

思路:

假设给定了数据:\([1, 2, 3, 4, 5, 6, 7 ,8]\)

我们可以很轻松地求出质数 \(2、3、5、7\) 的欧拉函数:\(\phi(prime) = prime - 1\),当然 \(\phi(1) = 1\),这是显而易见的

再根据上面的推理第 4 点:

\(n\) 是两个互质的数的乘积:\(n = a \times b\),则 \(\phi(n) = \phi(a) \times \phi(b)\)

则若知道两个数 \(i、j\)\(\phi(i)\)\(\phi(j)\) ,其中 \(i\) 是质数,且 \(j \bmod i \neq 0\),说明 \(i、j\) 互质,所以 \(\phi(i \times j) = \phi(i) \times \phi(j)\)

但若 \(j \bmod i = 0\),说明 \(i\)\(j\) 的质因子,由上面的欧拉公式:

\(\phi(n) = n \times (1 - \frac{1}{p_1}) \times (1 - \frac{1}{p_2}) \times \cdots \times (1 - \frac{1}{p_n})\)

则 :

\(\phi(j) = j \times (1 - \frac{1}{p_1}) \times (1 - \frac{1}{p_2}) \times \cdots \times (1 - \frac{1}{p_n})\)

\(\phi(j \times i) = j \times i \times (1 - \frac{1}{p_1}) \times (1 - \frac{1}{p_2}) \times \cdots \times (1 - \frac{1}{p_n}) = \phi(j) \times i\)

因此我们可以借助欧拉筛质数法来求出 \(1 \sim n\) 内所有数的欧拉函数之和。

代码参考
#include <bits/stdc++.h>

using namespace std;
int debuggersum = 0;
typedef long long ll;
typedef long double ld;
typedef __int128_t int128;

// 如何将自己的心中所想,用 「文字」、「语言」、「图像」 记录下来

const int N = int (1e7 + 10);
ll p[N], d[N], e[N], cnt;
int n;

void get_p(int n)
{
    e[1] = 1;
    for (int i = 2; i <= n; i ++)
    {
        if (d[i] == 0) p[cnt ++] = i, e[i] = i - 1;
        for (int j = 0; p[j] <= n / i; j ++)
        {
            d[p[j] * i] = 1;
            if (i % p[j] == 0)
            {
                e[p[j] * i] = p[j] * e[i]; break;
            } 
            else e[p[j] * i] = e[p[j]] * e[i];
        }
    }
}

void solve(void)
{
    scanf ("%d", &n);
    get_p(n);
    ll ans = 0;
    for (int i = 1; i <= n; i ++) ans += e[i];
    printf ("%lld\n", ans);
}

// 并能使读者与我达成共鸣,是我为什么而学习的最本质的追求

int main(void)
{
    solve();
}