【数论】—— 欧拉函数
欧拉函数
在做关于素数的题目时经常需要用到关于欧拉函数的知识,并且求法相似,所以建议先看我的素数 blog。
求单个数的欧拉函数
定义
对于任意正整数 n n n,欧拉函数 φ ( n ) \varphi(n) φ(n) 表示在不大于 n n n 的正整数中与 n n n 互素的数的个数。
举几个例子:
- φ ( 1 ) = 1 \varphi(1)=1 φ(1)=1,即 1 1 1。
- φ ( 6 ) = 2 \varphi(6)=2 φ(6)=2,即 1 1 1, 5 5 5。
- φ ( 12 ) = 4 \varphi(12)=4 φ(12)=4,即 1 1 1, 5 5 5, 7 7 7, 11 11 11。
怎样求出一个数的欧拉函数呢?公式呈上:
对于任意正整数 n n n,若 n = P 1 k 1 × P 2 k 2 × ⋯ × P m k m n=P_1^{k_1} \times P_2^{k_2} \times \cdots \times P_m^{k_m} n=P1k1×P2k2×⋯×Pmkm。则有 φ ( n ) = n × ( 1 − 1 P 1 ) × ( 1 − 1 P 2 ) × ⋯ × ( 1 − 1 P m ) \varphi(n)=n\times (1-\frac{1}{P_1})\times (1-\frac{1}{P_2})\times \cdots \times (1-\frac{1}{P_m}) φ(n)=n×(1−P11)×(1−P21)×⋯×(1−Pm1)。
化简得: φ ( n ) = ∏ i = 1 m ( 1 − 1 p i ) \varphi(n)=\prod_{i=1}^m (1-\frac{1}{p_i}) φ(n)=∏i=1m(1−pi1)。
有这个公式,我们可以求出单个正整数的欧拉函数。即在分解素因数时计算欧拉函数。
时间复杂度: O ( n ) O(\sqrt{n}) O(n)。
代码
注意:在素因数分解完后如果 n n n 变成了素数,那 a n s ans ans 还要再运算一次。
inline int phi(int n) {
int ans = n;
for(register int i = 2;i <= sqrt(n);i ++)
if(n % i == 0) {
ans = ans / i * (i - 1);
while (n % i == 0)
n /= i;
}
if(n > 1)
ans = ans / n * (n - 1);
return ans;
}
欧拉函数公式的推导过程
接下来,我们一起推导在 O ( n ) O(\sqrt{n}) O(n) 时间内求出欧拉函数的公式。
首先要知道几个性质:
-
对于任意素数 x x x,有 φ ( x ) = x − 1 \varphi(x)=x-1 φ(x)=x−1。这个很好理解,即 x x x 与所有小于它的数互素。
-
对于任意素数 p p p,有 φ ( p k ) = p k − p k − 1 \varphi(p^k)= p^k-p^{k-1} φ(pk)=pk−pk−1。为什么呢?
证明:因为 p p p 是素数,所以 p k p^k pk 的素因数只含有 p p p。那么在小于等于 p k p^k pk 的数中与其不互素的数便是 p p p, 2 × p 2\times p 2×p, 3 × p 3\times p 3×p, ⋯ \cdots ⋯, p k − 1 × p p^{k-1}\times p pk−1×p,共 p k − 1 p^{k-1} pk−1 个。得证。
-
欧拉函数是积性函数,但不是完全积性函数。这意味着 φ ( m × n ) = φ ( m ) × φ ( n ) \varphi(m\times n)=\varphi(m)\times \varphi(n) φ(m×n)=φ(m)×φ(n) 成立的前提是 gcd ( m , n ) = 1 \gcd(m,n)=1 gcd(m,n)=1。
了解完这几个性质后,我们开始推导:
设 N = p 1 k 1 × p 2 k 2 × p 3 k 3 × ⋯ × p n k n N=p_1^{k_1}\times p_2^{k_2}\times p_3^{k_3}\times \cdots \times p_n^{k_n} N=p1k1×p2k2×p3k3×⋯×pnkn。
因为 p 1 , p 2 , p 3 , ⋯ , p n p_1,p_2,p_3,\cdots,p_n p1,p2,p3,⋯,pn 都是素数,所以 p 1 k 1 , p 2 k 2 , ⋯ , p n k n p_1^{k_1},p_2^{k_2},\cdots,p_n^{k_n} p1k1,p2k2,⋯,pnkn 两两互素。
所以由性质 3 得:
φ ( N ) = φ ( p 1 k 1 ) × φ ( p 2 k 2 ) × ⋯ × φ ( p n k n ) \varphi(N)=\varphi(p_1^{k_1})\times \varphi(p_2^{k_2}) \times \cdots \times \varphi(p_n^{k_n}) φ(N)=φ(p1k1)×φ(p2k2)×⋯×φ(pnkn)
再由性质 2 得:
φ ( N ) = ( p 1 k 1 − p 1 k 1 − 1 ) × ( p 2 k 2 − p 2 k 2 − 1 ) × ⋯ × ( p n k n − p n k n − 1 ) \varphi(N)=(p_1^{k_1}-p_1^{k_1-1})\times(p_2^{k_2}-p_2^{k_2-1})\times\cdots\times(p_n^{k_n}-p_n^{k_n-1}) φ(N)=(p1k1−p1k1−1)×(p2k2−p2k2−1)×⋯×(pnkn−pnkn−1)
提取公因数得:
φ ( N ) = p 1 k 1 × p 2 k 2 × ⋯ × p n k 2 × ( 1 − 1 p 1 ) × ( 1 − 1 p 2 ) × ⋯ × ( 1 − 1 p n ) \varphi(N)=p_1^{k_1}\times p_2^{k_2}\times \cdots \times p_n^{k_2} \times (1-\frac{1}{p_1})\times (1-\frac{1}{p_2})\times\cdots\times (1-\frac{1}{p_n}) φ(N)=p1k1×p2k2×⋯×pnk2×(1−p11)×(1−p21)×⋯×(1−pn1)
最后把前 n n n 项合并一下便得到:
φ ( N ) = N × ( 1 − 1 p 1 ) × ( 1 − 1 p 2 ) × ⋯ × ( 1 − 1 p n ) \varphi(N)=N\times(1-\frac{1}{p_1})\times (1-\frac{1}{p_2})\times\cdots\times (1-\frac{1}{p_n}) φ(N)=N×(1−p11)×(1−p21)×⋯×(1−pn1)
推导完毕。
欧拉函数的线性筛法
和素数一样,这样的算法求单个数的欧拉函数时间复杂度很优,但当题目需要求一串数的欧拉函数时便处理不了了。此时的时间复杂度便是 O ( n n ) O(n\sqrt{n}) O(nn)。
那求一串数欧拉函数是否和求素数一样,有跟快甚至线性的方法呢?当然有,由刚刚的几个性质,我们可以在线性筛的基础上改进,在 O ( n ) O(n) O(n) 的时间内求得每个数的欧拉函数。
这里我们需要再引入一个性质:
-
若 i m o d p = 0 i\bmod p=0 imodp=0,则有: φ ( p × i ) = φ ( i ) × p \varphi(p\times i)=\varphi(i)\times p φ(p×i)=φ(i)×p。
证明:
设 b = gcd ( n , i ) b=\gcd(n , i) b=gcd(n,i);
因为 n n n, i i i 不互素,所以 n = k 1 b n=k_1b n=k1b, i = k 2 b i=k_2b i=k2b。
因为 n + i = ( k 1 + k 2 ) b n+i=(k_1+k_2)b n+i=(k1+k2)b,所以 n + i n+i n+i 与 i i i 不互素。
[ 1 , i ] [1,i] [1,i] 中与 i i i 不互素的整数 n n n 共 i − φ ( i ) i-\varphi(i) i−φ(i) 个。
因为 n + i n+i n+i 与 i i i 不互素,所以 [ 1 + i , i + i ] [1+i,i+i] [1+i,i+i],即 ( i , 2 i ] (i,2i] (i,2i] 中与 i i i 不互素的整数也是 i − φ ( i ) i-\varphi(i) i−φ(i) 个,所以 [ 1 , i + p ] [1,i+p] [1,i+p] 中与 i i i 不互素的整数 p × i − p × φ ( i ) p\times i-p\times \varphi(i) p×i−p×φ(i) 个。
因为 i m o d p = 0 i\bmod p = 0 imodp=0, i i i 与 p p p 没有不同的因数, [ 1 , i × p ] [1,i\times p] [1,i×p] 中与 i × p i\times p i×p 不互素的整数数量 p × i − p × φ ( i ) = i × p − φ ( i × p ) p\times i-p\times \varphi(i)=i\times p-\varphi(i\times p) p×i−p×φ(i)=i×p−φ(i×p)。
所以: φ ( i × p ) = p × φ ( i ) \varphi(i\times p)=p\times \varphi(i) φ(i×p)=p×φ(i)。
我们一起边回顾线性筛代码,边了解求欧拉函数的方法:
定义phi[MAXN]
存储欧拉函数。
首先,在开始筛之前我们先需要特判 1 1 1 的情况。同样,在求欧拉函数时,要先把 φ ( 1 ) \varphi(1) φ(1) 赋值为 1 1 1。
is_prime[1] = true;
phi[1] = 1;
然后我们开始找素数。在找的过程中,如果遇到了素数 x x x,那我们就存储下 x x x。同时,由之前讲的性质 1 1 1 得 φ ( x ) = x − 1 \varphi(x)=x-1 φ(x)=x−1。所以我们便可以对它的欧拉函数进行赋值。
for(register int i = 2;i <= n;i ++) {
if(!is_prime[i]) {
prime[++ cnt] = i;
phi[i] = i - 1;
}
...
}
最后,在用找到的数 x x x 来筛素数的过程中。
若用来与 x x x 相乘的素数 y y y 不是 x x x 的最小的素因数,即比 x x x 的最小的素因数小,则说明 x x x 不包含 y y y 这个素因数,所以 gcd ( x , y ) = 1 \gcd(x,y)=1 gcd(x,y)=1。此时,由性质 3 3 3 得到: φ ( x × y ) = φ ( x ) × φ ( y ) \varphi(x\times y)=\varphi(x)\times\varphi(y) φ(x×y)=φ(x)×φ(y)。
若用来与 x x x 相乘的素数 y y y 已经与 x x x 不互素了,即此时 y y y 是 x x x 的最小素因数。则根据性质 4 4 4 ,有: φ ( x × y ) = φ ( x ) × y \varphi(x\times y)=\varphi(x)\times y φ(x×y)=φ(x)×y。
for(register int j = 1; j <= cnt , i * prime[j] <= n; j ++) {
is_prime[i * prime[j]] = true;
if(i % prime[j] == 0) {
phi[i * prime[j]] = phi[i] * prime[j];
break;
}
phi[i * prime[j]] = phi[i] * phi[prime[j]];
}
完整代码
int phi[MAXN] , prime[MAXN] , cnt;
bool is_prime[MAXN];
inline void Init_phi(int n) {
is_prime[1] = true;
phi[1] = 1;
for(register int i = 2;i <= n;i ++) {
if(!is_prime[i]) {
prime[++ cnt] = i;
phi[i] = i - 1;
}
for(register int j = 1;j <= cnt , i * prime[j] <= n;j ++) {
is_prime[i * prime[j]] = true;
if(i % prime[j] == 0) {
phi[i * prime[j]] = phi[i] * prime[j];
break;
}
phi[i * prime[j]] = phi[i] * phi[prime[j]];
}
}
return;
}