欧拉函数

「观前提醒」

「文章仅供学习和参考,如有问题请在评论区提出」

欧拉函数#


定义#


欧拉函数的符号表示是 φ(n) ,表示 1n 中和 n 互质的数的个数。

例如,φ(12)=4,即 1,5,7,11

性质#


  1. n 是质数, 则 φ(n)=n1

    质数会和小于它本身的所有正整数互质,即 n1n1 中所有数互质。

  2. n 是奇数时,φ(2n)=φ(n)

    只有这一种情况成立,并不是 n 的偶数倍的意思。

  3. 如果 n=pk,其中 p 是质数,那么

    (1)φ(n)=pkpk1(2)=pk1(p1)(3)=pk(11p)

    1n 中只有不包含质数 p,才会与 n 互质。而包含质数 p 的数为 p 倍数,即 1p,2p,3p,4p,...,pk1p ,总共有 pk1 个。

    所以去掉包含 p 的数,就是和 n 互质的数的个数,即 φ(n)=pkpk1

    公式变形,就会有上述三个表示方式。

  4. 积性函数:若 gcd(a,b)=1,则 φ(ab)=φ(a)φ(b)

计算公式推导#


由唯一分解定理,

n=i=1kpiai=p1a1p2a2p3a3...pkak

pin 的质因子

提醒:ab 是乘积运算符号,代表 ab 所有数的乘积,即 a×(a+1)×...×b

那么有,

φ(n)=φ(i=1kpiai)

因为对于任意的 piaipjaj(1i,jk) 都是互质的,所以用到上面的性质4:若 gcd(a,b)=1,则 φ(ab)=φ(a)φ(b) 。那么可以推出,

(4)φ(n)=φ(i=1kpiai)(5)=i=1kφ(piai)

然后再根据性质3,推出

(6)φ(n)=φ(i=1kpiai)(7)=i=1kφ(piai)(8)=i=1kpiai1(pi1)(9)=i=1kpiai(11pi))

最后进行公式变形,可得

(10)φ(n)=φ(i=1kpiai)(11)=i=1kφ(piai)(12)=i=1kpiai1(pi1)(13)=i=1kpiai(11pi)(14)=i=1kpiai×i=1k(11pi)(15)=n×i=1kpi1pi(16)=n×p11p1×p21p2×...×pk1pk

公式进行整理,可得

(17)φ(n)=n×i=1kpi1pi(18)=n×p11p1×p21p2×...×pk1pk

观察公式就能发现,欧拉函数仅与 n 及其质因子有关

求欧拉函数#


分解质因子法#


思路:用试除法分解出 n 的所有质因子,然后根据推导的公式求解一个数的欧拉函数。

时间复杂度O(n)

代码

// 分解质因数求欧拉函数
int phi(int n) {
    int res = n;
    // 分解质因子
    for (int i = 2; i <= n / i; i++) {
        if (n % i == 0) {
            // 公式求值
            res = res / i * (i - 1);
            while (n % i == 0) n /= i;
        }
    }
    if (n > 1) res = res / i * (i - 1);
    
    return res;
}

筛法求欧拉函数#


// 线性筛法求 1 ~ n 的 质数

const int N = 1e5 + 10;
int p[N], cnt;	// p[]存储所有素数
bool st[N];	 // st[x]存储x是否被筛掉

void get_primes(int n) {
	st[1] = true;
    
    for (int i = 2; i <= n; i++) {
        if (!st[i]) p[cnt++] = i;
        for (int j = 1; p[j] <= n / i; j++) {
            st[p[j] * i] = true;
            if (i % p[j] == 0) break;
        }
    }
}

思路

观察上面的线性筛质数的代码,我们可以再用一个 phi[] 来存储每一个数的欧拉函数,即 phi[i]=φ(i)

i 是质数,根据性质1可得 phi[i]=i1

而且在线性筛中,每个合数(非质数)都会被自身的最小质因子筛掉。那么设 pjm 的最小质因子,根据线性筛,就想办法让 m 通过 m=pj×i 筛掉。

  • i 能被 pj 整除,则 i 就包含了 m 的所有质因子。

    i 能被 pj 整除,说明 pj 也是 i 的质因子。又因为 pj 也是 m 的质因子,而且 m=pj×i ,所以 i 就包含了 m 的所有质因子。

    然后再根据推导的公式变形,得

    (19)φ(m)=m×k=1Spk1pk(20)=pj×i×k=1Spk1pk(21)=pj×φ(i)

    根据推导公式,一个数得欧拉函数只与其本身和其质因子有关。

    虽然 k=1Spk1pk 是从 φ(m) 推导出来的,但是因为 im 的质因子相同,所以也可以被用来推导出 φ(i)

  • i 不能被 pj 整除,则 ipj 是互质的。

    因为 pj 是质数,除了 pj 本身,就没有其他的质因子。若 i 不能被 pj 整除,那么 ipj 就没有相同的质因子,那么两者就是互质的。

    然后根据性质4和性质1进行公式变形,得

    (22)φ(m)=φ(pj×i)(23)=φ(pj)×φ(i)(24)=(pj1)×φ(i)

总结上面的思路,就能得到所有的情况,

  • i 是 质数,得 φ(i)=i1
  • i 不是质数,说明已经被自己的质因子赋值了。
  • 遍历 p[1cnt]
    • i 能被 pj 整除,得 φ(m)=pj×φ(i) ,并退出遍历。
    • i 不能被 pj 整除,得 φ(m)=(pj1)×φ(i)

时间复杂度O(N)

代码

const int N = 1e5 + 10;
int p[N], cnt;	// p[] 存储所有素数
int phi[N];	// phi[x] 存储 x 的欧拉函数值
bool st[N];	// st[x] 存储 x 是否被筛掉

// 线性筛法求欧拉函数
void get_phi(int n) {
    phi[1] = 1;
    st[1] = true;
    
    for (int i = 2; i <= n; i++) {
        // 没有被筛过,说明是质数
        if (!st[i]) {
            p[cnt++] = i;
            phi[i] = i - 1;
        }
        
        for (int j = 0; p[j] <= n / i; j++) {
            int m = i * p[j];
          	st[m] = true;
            // 判断是否能整除,然后根据公式赋值
            if (i % p[j] == 0) {
                phi[m] = p[j] * phi[i];
                break;
            } else phi[m] = (p[j] - 1) * phi[i];
        }
    }
}


参考资料#


欧拉函数 – OI Wiki (oi-wiki.org):https://oi-wiki.org/math/number-theory/euler/

【RSA原理2】浅谈–什么是欧拉函数 韦_恩的博客-CSDN博客:https://blog.csdn.net/qq_42539194/article/details/118514310

董晓算法 515 筛法求欧拉函数:https://www.bilibili.com/video/BV1VP411p7Bs


© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYRgtvf9' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片