定义

$\varphi(x)$ 指对于 $\forall i\in [1,x]$ ,满足 $\gcd(i,x)=1$ 的 $i$ 的个数。

也就是小于等于 $x$ 的正整数中与其互质的数的个数。


基本推论

$\varphi(x)=x-1\quad(x\in \mathbb{P})$


$ \begin{aligned} \varphi(p^k)& =p^k-p^{k-1} \quad(p\in \mathbb{P},k\in \Z^*) \\ &= p^k(1-\frac{1}{p})\quad(p\in \mathbb{P},k\in \Z^*)\end{aligned}$

这是因为只有当一个数不包含质数 $p$ ,才可能与 $x$ 互质。而包含质数 $p$ 的数一共有$ p^{k-1} $个,即 $1×p、2×p、3×p、…、p^{k-1}×p$ ,把它们去除,剩下的就是与n互质的数。


若 $x=p_1 \times p_2 \quad (\gcd(p_1,p_2)=1)$

则 $\varphi(x)=\varphi(p_1p_2) = \varphi(p_1)\varphi(p_2)$

证明较为复杂。


由上式推广

$n=p_1^{k_1}p_2^{k_2}…p_x^{k_x}\quad(p_1,p_2… p_n\in \mathbb{P})$

$ \begin{aligned} \varphi(n)&=\varphi(p_1^{k_1})\varphi(p_2^{k_2})…\varphi(p_x^{k_x})\\ &= p_1^{k_1}p_2^{k_2}…p_x^{k_x}(1-\frac{1}{p_1})(1-\frac{1}{p_2})…(1-\frac{1}{p_x}) \\ &= n(1-\frac{1}{p_1})(1-\frac{1}{p_2})…(1-\frac{1}{p_x}) \end{aligned}$

于是我们就得到了欧拉函数的通项

$$\varphi(n)=n\prod\limits_{d\in \mathbb{P}\&d|n}^n(1-\frac{1}{d})$$

由通项我们可以推导,若 $y|x$ ,则

$\varphi(x\times y)=\varphi(x) \times y$


由通项我们还可以推出

$\varphi(n!)=\begin{cases} \varphi((n-1)!)\times(n-1)&n\in \mathbb{P} \\\varphi((n-1)!)\times n & n\notin\mathbb{P} \end{cases}$

可以在 $O(n)$ 时间内求解 $\varphi(n!)$ 。


计算

  • 单个求法

在上文中我们推导出了欧拉函数通项,

配合质数筛可以在 $O(因子个数)$ 内求出 $\varphi(n) $。

int phi(int x){
	int ans=x;
	for(int i=1;i<=cnt&&prime[i]*prime[i]<=x;++i){
		if(x%prime[i]) continue;
		ans=ans/prime[i]*(prime[i]-1);
		while(x%prime[i]==0) x/=prime[i];
	}
	if(x>1) ans=ans/x*(x-1);
	return ans;
}
  • 多个求法

然而我们在某些时候需要求出连续区间内的所有 $\varphi(i)$ 值,这时候上述求法的复杂度就有些劣

涉及 $10^6$ 及以上的数据时容易被卡常,因此我们需要更快的方式。

涉及到线性,质数,我们可以自然联想到欧拉筛。

回忆一下欧拉筛的实现步骤:

  1. 遍历每个 $x$ ,观察是否为质数。
  2. 使用先前筛出的质数和遍历到的 $x$ ,筛掉接下来的合数。

我们知道,欧拉筛的过程是线性的,也就是每个数最多遍历一遍,利用这个性质,我们可以做到线性求解。

第一步中,对于 $x\in \mathbb{P}$ ,$\varphi(x)=x-1$

第二步中,对于合数,我们发现:

对于任意 $y=x*p\quad(p\in \mathbb{P} \&x> p)$, 总有

$\varphi(y)=\varphi(x)\times \varphi(p)\quad(p\nmid x)$

$\varphi(y)=\varphi(x)\times p\quad(p\mid x)$

然后我们就可以代码实现。

void init(){
	isp[0]=isp[1]=1;phi[1]=1;
	for(int i=2;i<N;++i){
		if(!isp[i]) pri[++cnt]=i,phi[i]=i-1;
		for(int j=1;j<=cnt&&pri[j]*i<=N;++j){
			isp[pri[j]*i]=1;
			if(i%pri[j]) phi[pri[j]*i]=phi[i]*phi[pri[j]];
			else phi[pri[j]*i]=pri[j]*phi[i];
			if(i%pri[j]==0) break;
		}
	}
	//for(int i=1;i<N;++i) sum[i]=sum[i-1]+phi[i];
}

参考资料

https://blog.csdn.net/wrwhahah/article/details/82704053

https://www.cnblogs.com/bibibi/p/10257292.html