欧拉函数
1 ~ N 中与 N 互质的数(互质是公约数只有1的两个整数,叫做互质)的个数被称为欧拉函数,记为ϕ(N)。
若在算数基本定理中,\(N=p_1^{\alpha_1}p_2^{\alpha_2}\cdots{p_m^{\alpha_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);