一、欧拉函数概念
\(ϕ(n)\)表示\(n\)的欧拉函数值,代表了从\(1\)到\(n\)与\(n\)互质的数的个数,例如\(ϕ(8)=4\) 因为\(1,3,5,7\)均和\(8\)互质。特殊的\(ϕ(1)=1\)
二、欧拉函数公式
根据算法基本定理,数字\(n\)必然可以描述为如下数学公式:
其中\(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;
}
五、欧拉函数的证明
欧拉函数的证明是使用的融斥原理,从定义出发:
(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})\)展开,发现就是上面的东东了,证明完毕。