「欧拉函数」学习笔记

欧拉函数φ(n)表示整数1n中与n互质的数的个数。

 

特殊情况

1. φ(1)=1

2. 当n为素数时,φ(n)=n1.

3. 若n是素数pk次幂,φ(n)=φ(pk)=pkpk1=(p1)pk1

(除掉p的倍数即可,由于pk=pk1p,故共有pk1p的倍数)

性质1. 当m,n互质时,φ(mn)=φ(m)φ(n)

这就是欧拉函数的积性性质。欧拉函数是积性函数

设有质数p1,p2,则φ(p1k1p2k2)按照刚才的方法,总数减去p1的倍数与p2的倍数,再加上重复减掉的p1,p2的公倍数。

p1的倍数有1p1,2p1,...,p1k11p2k2p1,总共有p1k11p2k2个。

同理p2的倍数有p1k1p2k21个。

由于p1,p2lcm(p1,p2)=p1p2p1p2的倍数有1p1p2,2p1p2,...,p1k11p2k21p1p2,总共有p1k11p2k21

因此我们得到φ(p1k1p2k2)=p1k1p2k2(p1k11p2k2)(p1k1p2k21)+p1k11p2k21

然后再按照刚才的思路,分开考虑:

φ(p1k1)=p1k1p1k11

φ(p2k2)=p2k2p2k21

φ(p1k1)φ(p2k2)=p1k1p2k2(p1k11p2k2)(p1k1p2k21)+p1k11p2k21

这两个式子是一模一样的,所以φ(p1k1p2k2)=φ(p1k1)φ(p2k2)

所以φ(mn)=φ(n)φ(m)

 

性质2. 欧拉函数的通式 φ(n)=ni=1rpi1pi

证明:刚才我们得到了φ(pk)=(p1)pk1,又得到了欧拉函数的积性性质φ(mn)=φ(n)φ(m),也可以以此类推三个,四个……

   因此对于任意一个φ(n),我们可以对n进行质因数分解:

      n=p1k1p2k2...prkr

   对于这个n,我们依然使用这个方法,得到φ(n)=φ(p1k1)φ(p2k2)...φ(prkr)

   因此就得到了φ(n)=(p1k1p1k11)(p2k2p2k21)...(prkrprkr1)

    φ(n)=i=1rpiki1(pi1)

         =i=1rpikipi1(pi1)

          =ni=1rpi1pi

   因此我们得到通式φ(n)=ni=1rpi1pi

  有了通式,我们就可以求欧拉函数φ(n)了:

复制代码
int a,b,n,phi[N];
main(){
    n = r;
    if(n == 2){ printf("1"); return 0; }
    for(int i = 1; i <= n; ++i) phi[i] = i;
    for(int i = 2; i <= n; ++i){
        if(phi[i] != i) continue;
        for(int j = i; j <= n; j += i) phi[j] = phi[j]/i*(i-1);
    }
    printf("%lld", phi[n]);
    return 0;
}
复制代码

    以上代码复杂度是O(n)的,顺便充当筛素数了。如果phi[i]=i则说明是素数。然后对n之内所有以i为因子的数都记性上述公式描绘的操作。注意到我们先做了/i,然后再做乘。这样是为了避免数据过大而造成数据丢失。

性质3. φ(px)=φ(x)p (p|x) (x, p为整数)

证明:由通式可得 φ(px)=pxi=1rai1ai 

   观察通式我们发现,一个数的欧拉函数其实只和其各个素因数的种类有关,并不关心每种素因数有几个。那么只需要证明φ(x)=xi=1rai1ai,也就是 a1 ar包含了xxp的所有种类的素因数 就好了。

   那么由于p|x,说明px的约数,所以p的所有约数必然包括在x之内,所以x的所有种类的素因数都是px的所有种类的素因数是必然的。

   所以我们可以继续推:

   φ(px)=xpi=1rai1ai

   φ(px)=φ(x)p

 

总结一下,当p为质数时:若p|x,可以利用性质5推得φ(px)。否则x%p0,又因为p是质数,所以x,p一定互质,所以用性质6就可以推得φ(px)。因此当p为质数时,无论如何都可以推得φ(px),因此我们可以利用在线性筛素数的基础上线性完成欧拉函数的求解。

 

复制代码
/*
 * phi[i]用来表示欧拉函数
 * isprime[i]用来记录i是否是质数 
 * p数组用来存质数 
 */
int n,tot;
int phi[N],isprime[N],p[N]; 
int main(){
    n = r;
    phi[1] = 1;
    for(int i = 2; i <= n; ++i){
        if(!isprime[i]){
            p[++tot] = i;
            phi[i] = i-1;    //若i是质数,则根据特殊情况2, \varphi(i) =  i-1
        }
        for(int j = 1; j <= tot; ++j){
            if(i * p[j] > n) break;  //越界 
            isprime[i * p[j]] = 1;   //存在因子i和p[j],故一定不是质数 
            if(i % p[j] == 0){
                phi[i * p[j]] = phi[i] * p[j]; /*性质5*/
                break;
                /*p[j]是i的素因子之一。由于i % p[j] == 0,所以p[j]也是i的素因子之一。
                 *因为p数组是递增的,因此p[j+1] > p[j],而i内包括了p[j]。p[j]已经作为i * p[j]的素因子了,
                 *所以p[j+1]就一定不是它的最小素因子了。 
                 */
            } 
            else phi[i * p[j]] = phi[i] * (p[j] - 1);
                /*利用性质6,p[j]是质数*/
        }
    }
    printf("%d",phi[n]);
    return 0;
}
复制代码

 

posted @   行而上  阅读(459)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· Apache Tomcat RCE漏洞复现(CVE-2025-24813)
点击右上角即可分享
微信分享提示