欧拉函数φ(x)表示小于等于x的正整数中与x互质的数的个数。比如φ(4)=2,因为4与1,3互质。
推导证明
下面说明欧拉函数的性质,再通过性质得出欧拉函数的公式。
命题1:φ(1)=1
证明:无
命题2:对于任意素数p,φ(p)=p-1
证明:每个素数的因子只有1和自身,故1,2,...,p中有1,2,...,p-1与p互质。
命题3:欧拉函数是积性函数,即对任意互质的正整数n,m,有φ(nm)=φ(n)φ(m)。
证明:我们将1~nm之间的正整数以矩阵形式展开:
$$ \left[\begin{matrix} 1 & 2 &\cdots & m-1 & m\\ m+1 & m+2 &\cdots & 2m-1 & 2m\\ \vdots &\vdots &\ddots &\vdots &\vdots\\ \left(n-2\right)m+1 &\left(n-2\right)m+2 &\cdots &\left(n-1\right)m-1 &\left(n-1\right)m\\ \left(n-1\right)m+1 &\left(n-1\right)m+1 &\cdots & nm-1 & nm \end{matrix}\right] $$
对于rm+c,其中1<=r<=n,1<=c<=m,如果c与m互质,那么rm+c则与m互质(同余定理),故我们可以找到φ(m)个数c1,c2,...,cφ(m),而其余1~m中的正整数由于与m有公约数,故与mn也必然有公约数,即不可能与mn互质。我们将除了c1,c2,...,cφ(m)外的其余列全部从矩阵中移除。
而对于c,m+c,...,(n-1)m+c,考虑到m与n互质,因此这里列出的n个数在模n剩余类环中均不同。事实上im+c=jm+c(mod n),有(i-j)m=0(mod n),而m与n互质,故i-j=kn,这里的i,j取值范围为0~(n-1),故i=j,因此这n个数在模n的意义下两两不同。而考虑到模n剩余类环的大小为n,因此这n个数恰好组成了整个环,即集合为{1,2,...,n-1}。考虑与n互质的φ(n)个数,r1,r2,...,rφ(n),我们发现每一列中都有且只有φ(n)个数与n互质。我们将每一列不能与n互质的数移去,这样总共留下了φ(n)φ(m)个数。
按照上面的说明我们得知最多只有φ(n)φ(m)个数与mn互质,而我们还需要保证矩阵中留下的φ(n)φ(m)个数都能与mn互质。考虑与mn非互质的数x,则mn与x有公约数p,即p|mn,考虑到m与n互质,因此或者p|m或者p|n,即x与m或n非互质。考虑到我们上面的过程将所有与m或n非互质的数全部移除掉了,因此矩阵中留下的数全都与mn互质。
命题4:对于任意素数p,φ(p^n)=(p^n)*(1-1/p)。
证明:在p^n中,能与p非互质的数为p,2p,...,p^n,共p^(n-1)个。因此我们得到φ(p^n)=p^n-p^(n-1)=(p^n)*(1-1/p)。
结合上面四条命题,对于任意一个数n,我们可以将其分解为素因子的幂的乘积$ n=p_1^{c_1}\cdots p_k^{c_k} $(算术基本定理),之后利用命题4与3推出:
$$ \varphi\left(n\right)=\varphi\left(p_1^{c_1}\right)\cdots\varphi\left(p_k^{c_k}\right)=p_1^{c_1}\left(1-1/p_1\right)\cdots p_k^{c_k}\left(1-1/p_k\right)=n\prod_{i=1}^k{\left(1-\frac{1}{p_i}\right)} $$
编程实现
计算欧拉函数有两种方式,一种是先预处理出所有素数,之后利用O(√n)的时间复杂度计算出欧拉函数φ(n)。第二种方式是利用欧拉筛在O(N)的时间和空间复杂度内计算1~N之间的所有欧拉函数。前者消耗的空间较少,且时间复杂度在只需要计算少量值的欧拉函数值时有利,而后者则适用于需要计算大批量欧拉函数值且N较小。
方式1:
phi(n) res = n for prime p in [1, sqrt(n)] if n % p != 0 continue res = res / p * (p - 1) while n % p == 0 n = n / p if n > 1 //此时n余下一个素数 res = res / n * (n - 1) return res
方式2:
init(limit) isPrime = empty-array phi = empty-array primes = empty-list for(i = 1; i < limit; i++) isPrime[i] = true phi[1] = 1 for(i = 2; i < limit; i++) if(isPrime[i]){ phi[i] = i - 1 primes.add(i) } for p in primes isPrime[i * p] = false if(i % p == 0) phi[i * p] = phi[i] * p break else phi[i * p] = phi[i] * phi[p] return phi