「欧拉函数」学习笔记
欧拉函数$\varphi(n)$表示整数$1到n$中与$n$互质的数的个数。
特殊情况
1. $\varphi(1) = 1$
2. 当$n$为素数时,$\varphi(n) = n-1$.
3. 若$n$是素数$p$的$k$次幂,$\varphi(n) = \varphi(p^k) = p^k - p^{k-1} = (p-1)p^{k-1}$
(除掉$p$的倍数即可,由于$p^k = p^{k-1} * p$,故共有$p^{k-1}$个$p$的倍数)
性质1. 当$m, n$互质时,$\varphi(mn) = \varphi(m)\varphi(n)$
这就是欧拉函数的积性性质。欧拉函数是积性函数
设有质数$ p_1, p_2 $,则$ \varphi(p_1^{k_1} * p_2^{k_2}) $按照刚才的方法,总数减去$p_1$的倍数与$p_2$的倍数,再加上重复减掉的$p_1, p_2$的公倍数。
$p_1$的倍数有$ 1 * p_1, 2 * p_1, ... , p_1^{k_1 - 1} * p_2^{k_2} * p_1 $,总共有$p_1^{k_1 - 1} * p_2^{k_2}$个。
同理$p_2$的倍数有$p_1^{k_1} * p_2^{k_2-1}$个。
由于$p_1, p_2互质$,$lcm(p_1, p_2) = p_1 * p_2$,$p_1 * p_2$的倍数有$ 1 * p_1 * p_2, 2 * p_1 * p_2 , ... , p_1^{k_1-1} * p_2^{k_2-1} * p_1 * p_2$,总共有$p_1^{k_1-1} * p_2^{k_2-1}$个
因此我们得到$ \varphi(p_1^{k_1} * p_2^{k_2}) = p_1^{k_1} * p_2^{k_2} - (p_1^{k_1 - 1} * p_2^{k_2}) - (p_1^{k_1} * p_2^{k_2-1}) + p_1^{k_1-1} * p_2^{k_2-1}$
然后再按照刚才的思路,分开考虑:
$\varphi(p_1^{k_1}) = p_1^{k_1} - p_1^{k_1-1}$
$\varphi(p_2^{k_2}) = p_2^{k_2} - p_2^{k_2-1}$
故$\varphi(p_1^{k_1}) * \varphi(p_2^{k_2}) = p_1^{k_1} * p_2^{k_2} - (p_1^{k_1 - 1} * p_2^{k_2}) - (p_1^{k_1} * p_2^{k_2-1}) + p_1^{k_1-1} * p_2^{k_2-1}$
这两个式子是一模一样的,所以$\varphi(p_1^{k_1} * p_2^{k_2}) = \varphi(p_1^{k_1}) * \varphi(p_2^{k_2})$
所以$\varphi(mn) = \varphi(n) * \varphi(m)$
性质2. 欧拉函数的通式 $\varphi(n) = n * \prod\limits_{i=1}^r \frac{p_i-1}{p_i}$
证明:刚才我们得到了$\varphi(p^k) = (p-1)p^{k-1}$,又得到了欧拉函数的积性性质$\varphi(mn) = \varphi(n) * \varphi(m)$,也可以以此类推三个,四个……
因此对于任意一个$\varphi(n)$,我们可以对$n$进行质因数分解:
$n = p_1^{k_1}p_2^{k_2}...p_r^{k_r}$
对于这个$n$,我们依然使用这个方法,得到$\varphi(n) = \varphi(p_1^{k_1})\varphi(p_2^{k_2})...\varphi(p_r^{k_r})$
因此就得到了$\varphi(n) = (p_1^{k_1}-p_1^{k_1-1}) * (p_2^{k_2}-p_2^{k_2-1}) ... (p_r^{k_r}-p_r^{k_r-1})$
$\varphi(n) = \prod\limits_{i=1}^r p_i^{k_i-1}(p_i-1)$
$ = \prod\limits_{i=1}^r p_i^{k_i} * p_i^{-1}(p_i-1)$
$ = n * \prod\limits_{i=1}^r \frac{p_i-1}{p_i}$
因此我们得到通式$\varphi(n) = n * \prod\limits_{i=1}^r \frac{p_i-1}{p_i}$
有了通式,我们就可以求欧拉函数$\varphi(n)$了:
int a,b,n,phi[N]; main(){ n = r; if(n == 2){ printf("1"); return 0; } for(int i = 1; i <= n; ++i) phi[i] = i; for(int i = 2; i <= n; ++i){ if(phi[i] != i) continue; for(int j = i; j <= n; j += i) phi[j] = phi[j]/i*(i-1); } printf("%lld", phi[n]); return 0; }
以上代码复杂度是$O(n)$的,顺便充当筛素数了。如果$ phi[i]=i $则说明是素数。然后对$n$之内所有以$i$为因子的数都记性上述公式描绘的操作。注意到我们先做了$/i$,然后再做乘。这样是为了避免数据过大而造成数据丢失。
性质3. $\varphi(p * x) = \varphi(x) * p\ (p | x)$ (x, p为整数)
证明:由通式可得 $\varphi(p * x) = p * x * \prod\limits_{i=1}^r \frac{a_i-1}{a_i}$
观察通式我们发现,一个数的欧拉函数其实只和其各个素因数的种类有关,并不关心每种素因数有几个。那么只需要证明$ \varphi(x) = x * \prod\limits_{i=1}^r \frac{a_i-1}{a_i}$,也就是 $a_1 ~ a_r$包含了$x$与$x*p$的所有种类的素因数 就好了。
那么由于$p | x$,说明$p$是$x$的约数,所以$p$的所有约数必然包括在$x$之内,所以$x$的所有种类的素因数都是$p * x$的所有种类的素因数是必然的。
所以我们可以继续推:
$ \varphi(p * x) = x * p * \prod\limits_{i=1}^r \frac{a_i-1}{a_i}$
$ \varphi(p * x) = \varphi(x) * p$
总结一下,当$p$为质数时:若$p|x$,可以利用性质5推得$\varphi(p * x)$。否则$x \% p ≠ 0$,又因为$p$是质数,所以$x, p$一定互质,所以用性质6就可以推得$\varphi(p * x)$。因此当$p$为质数时,无论如何都可以推得$\varphi(p * x)$,因此我们可以利用在线性筛素数的基础上线性完成欧拉函数的求解。
/* * phi[i]用来表示欧拉函数 * isprime[i]用来记录i是否是质数 * p数组用来存质数 */ int n,tot; int phi[N],isprime[N],p[N]; int main(){ n = r; phi[1] = 1; for(int i = 2; i <= n; ++i){ if(!isprime[i]){ p[++tot] = i; phi[i] = i-1; //若i是质数,则根据特殊情况2, \varphi(i) = i-1 } for(int j = 1; j <= tot; ++j){ if(i * p[j] > n) break; //越界 isprime[i * p[j]] = 1; //存在因子i和p[j],故一定不是质数 if(i % p[j] == 0){ phi[i * p[j]] = phi[i] * p[j]; /*性质5*/ break; /*p[j]是i的素因子之一。由于i % p[j] == 0,所以p[j]也是i的素因子之一。 *因为p数组是递增的,因此p[j+1] > p[j],而i内包括了p[j]。p[j]已经作为i * p[j]的素因子了, *所以p[j+1]就一定不是它的最小素因子了。 */ } else phi[i * p[j]] = phi[i] * (p[j] - 1); /*利用性质6,p[j]是质数*/ } } printf("%d",phi[n]); return 0; }