欧拉函数专题

欧拉函数专题

一、定义

定义:欧拉函数是小于n的数中与n互质的数的数目。

例如φ(8)=4,因为1,3,5,7均和8互质。

解释18是互质的。互质是指两个数的最大公约数为1,而18的最大公约数就是1,因此它们是互质的

二、公式

如果n的唯一分解式:n=p1r1p2r2...pkrk
有:

φ(n)=n×p11p1×p21p2×...×pk1pk

还是上面的栗子:

8=23

φ(8)=8(112)=4

小结
下面三个公式,都需要依赖唯一分解定理:

n=p1r1p2r2...pkrk

  • ① 约数个数:只与幂次相关,与质因子无关
    约数个数=(1+r1)×(1+r2)×...×(1+rk)

  • ② 约数和:与质数子和幂次都相关
    约数和= (p10+p11+p12+...+p1r1)×(p20+p21+p22+...+p2r1)×...×(pk0+pk1+pk2+...+pkrk)

  • ③ 欧拉函数公式,只与因子相关,与指数无关!
    欧拉函数=n×p11p1×p21p2×...×pk1pk

三、欧拉函数公式证明

欧拉函数的证明是使用的融斥原理,从定义出发:

定义:欧拉函数是小于n的数中与n互质的数的数目。

  • ① 对数字n进行质因数分解:

n=p1r1p2r2p3r3...pkrk

  • ② 如果一个数包含了p1这个因子,那么它肯定与n不互质,因为有p1这个公因子嘛,换句话说,就是要想与n互质,那么一定不能包含p1,p2,...,pk这些因子。

小于n的所有数字中,如果它包含了p1,p2,...,pk这样的因子的话,那么它就与n不互质,换句话说,只要把小于n之内,所有包含p1,p2,..pk的数字都干掉,剩下的就是与n互质的数。所谓包含p1,p2,..,pk其实也就是 p1,p2,..,pk的整数倍。

  • p1的倍数从n中减去,那需要减去多少个呢?

答:np1个.

:这里想不明白的话,可以举个栗子:比如n=10p1=2,有几个p1的倍数呢?5个!为什么呢?

102=5

  • ③ 把p2,p3,...,pk的倍数都减去吧,分别减去np2,np3,...,npk个。

  • ④ 这么干是减多了的,比如某个数,是p2的倍数,也是p3的倍数,就减了两回,还需要再加回来pipj的倍数,就是 + np1p2+ np1p3+ np1pk+ ....

-⑤ 将公式ϕ(n)=n(11p1)(11p2)(11p3)...(11pk)展开,发现就是上面的东东了,证毕。

变形:
ϕ(n)=n(11p1)(11p2)(11p3)...(11pk)ϕ(n)=n(p11p1)(p21p2)(p31p3)...(pk1pk)

四、求单个数字的欧拉函数

AcWing 873. 欧拉函数

#include <bits/stdc++.h>
using namespace std;

// 求单个数的欧拉函数值
int phi(int x) {
    int res = x;
    // 不要写在i*i<=x, 可能会造成爆int
    for (int i = 2; i <= x / i; i++)
        if (x % i == 0) {              // 暴力枚举所有因子
            res = res / i * (i - 1);   // 套一下欧拉函数公式
            while (x % i == 0) x /= i; // 只与质因子有关,与幂次无关,应除尽除
        }
    if (x > 1) res = res / x * (x - 1); // 最后一个大因子
    return res;
}

int main() {
    int n;
    cin >> n;
    while (n--) {
        int x;
        cin >> x;
        printf("%d\n", phi(x));
    }
    return 0;
}

五、线性筛法求欧拉函数

依托于线性筛法,可以顺带求出欧拉函数值。如果对数论了解更多,会知道线性筛法还可以求出很多其它的东西。

  • phi[1]=1
    φ(1)定义1

对区间内每个数进行分情况讨论:

  • 如果这个数是质数,那么质数i的欧拉函数值是phi[i]=i1
    比如7,那么16当中有多少个数与7互质呢?显然6个都是嘛。

  • 如果这个数不是质数,那么数字i在被primes[j]尝试筛的过程中:(这里设 pj=primes[j]以简便下面的书写)

推论1:如果i % pj=0, 那么 phi[i×pj]=phi[i]×pj

证明:
i=p1r1p2r2p3r3...pjrj...pkrk 【算术基本定理】

i×pj分解质数因数的结果,只比i多分解了一个pj,而 i % pj=0 说明i中存在pj因子,只不过指数增加了1个。

i×pj=p1r1×p2r2×p3r3×...×pjrj+1×...×pkrk

ϕ(i)=i×(11p1)×(11p2)×(11p3)×...×(11pj) 【欧拉公式】

我们发现,欧拉公式只与质数因子相关,而与质数因子的幂次无关! 二者的质数因子没有变化,变化的只是某个质数因子的幂次。所以:

ϕ(i×pj)=i×pj×(11p1)×(11p2)×(11p3)×...×(11pj)=ϕ(i)×pj

证毕

推论2: 如果i % pj>0, 那么 phi[i×pj]=phi[i]×(pj1)

证明:
i=p1r1p2r2p3r3...pkrk

ϕ(i)=i×(11p1)×(11p2)×(11p3)×...×(11pk)

pji分解质数因数的结果,只是比i多分解了一个pj,而 i % pj>0 说明i中不存在pj这个因子,需要写上去。

pj×i=p1r1×p2r2×p3r3×...×pkrk×pj1

ϕ(pji)=pji(11p1)(11p2)(11p3)...(11pk)(11pj)

ϕ(pji)=pj×ϕ(i)×(11pj)=ϕ(i)×(pj1)

【证毕】

AcWing 874. 筛法求欧拉函数

Code

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e6 + 10;

// 筛法求欧拉函数
// 这个东西就在欧兰筛的基础上,加上题解中证明的公式,只能是背下来,没有别的办法
int primes[N], phi[N], st[N];
int cnt, res;
void get_eulers(int n) {
    phi[1] = 1; // 1的欧拉函数值是1,这个是递推的起点
    for (int i = 2; i <= n; i++) {
        if (!st[i]) {
            primes[cnt++] = i;
            phi[i] = i - 1; // ①质数,则phi(i)=i-1
        }
        for (int j = 0; primes[j] <= n / i; j++) {
            int t = primes[j] * i;
            st[t] = 1;
            if (i % primes[j] == 0) {
                phi[t] = phi[i] * primes[j]; // ② i%pj==0
                break;
            } else
                phi[t] = phi[i] * (primes[j] - 1); // ③i%pj>0
        }
    }
}

signed main() {
    int n;
    cin >> n;
    // 筛法求欧拉函数
    get_eulers(n);
    // 累加欧拉函数值
    for (int i = 1; i <= n; i++) res += phi[i];
    printf("%lld\n", res);
}

六、欧拉定理

na 为正整数,且 n,a 互素,即gcd(n,a)=1,则:

aϕ(n)1(mod n)

讲解视频
证明挺麻烦的,有兴趣可以看一看,我就不证明了~

用途

这个定理可以用来 简化幂的模运算

基本例子

比如计算7222的个位数,实际是求722210除的余数。710 互素 ,且φ(10)=4。由欧拉定理知

741(mod 10) (a=7,n=10)

所以7222=(74)557215572499(mod 10)

所以,答案是9

复杂例子

33335555个位 是什么。

很明显不可能直接把33335555的结果计算出来,那样太大了。但我们想要确定的是33335555,问题就简单了:

  • 我们知道 33335555= 35555%10

  • 因为要求的是末位,也就是%10的结果,想要使用 欧拉定理:
    • 310互质
    • ϕ(10)=4,需要想办法构造5555里面有多少个4,可以直接把4的倍数直接约掉:

      根据 模运算规则 (ab)%p=(a%pb%p)%p ,由于5555=41388+3,我们得到

      35555%10=(34138833)%10=(341388%1033%10)%10=(341388%1027%10)%10=(341388%107)%10

      欧拉定理 出场!

      对于341388而言, 341388=(34)1388

      (34)ϕ(n)=ϕ(10)=4,即 n=10(1379满足);

      同时 满足 310互质,所以 341388=(1%10)1388=1

      所以(341388%107)%10=(17)%10=7

      计算完毕,答案是7

七、一些性质

性质1

gcd(n,m)=1时, ϕ(nm)=ϕ(n)×ϕ(m)

从定义上来证明:

假设有两个互质的正整数n,m,则

ϕ(n)=n(11pi)

ϕ(m)=m(11pi)

ϕ(n)×ϕ(m)=n(11pi)×m(11pi)=nm(11pi)(11pi)

因为n,m互质,所以pipi各各都不相同,且都是n,m的质因子
因此就可以推出φ(nm)=φ(n)φ(m)

至此,积性函数的性质得证。但是由上面的证明可知,n,m必须要互质才可以满足欧拉函数是 积性函数,由此可见 欧拉函数不是完全积性函数

性质2

n 为奇数时, ϕ(2n)=ϕ(n)

证明
根据上面的性质2,当 n 为奇数时, n2 互质,

ϕ(2n)=ϕ(2)×ϕ(n)

又因为ϕ(2)=1
所以 ϕ(2n)=ϕ(n)

性质3

n=pk , 则 ϕ(n)=pkp1p=pkpk1=pk1(p1)

性质4

n>2时,所有小于n且与n互质的数和=n×ϕ(n)2,并且ϕ(n) 为偶数

证明
需要知道的一个基本事实是 gcd(n,x)=gcd(n,nx)    (n>x)

:关于这个,可以了解一下 更相减损术
也可以证明一下:
反证法,假设nx互质,nnx不互质
nnx的最大公约数为m,则n=pmnx=qm
所以x=(pq)m,推导出xn有公约数m与假设矛盾

因为 gcd(n,x)=gcd(n,nx)(n>x) ,所以与n互质的数都是 成对出现

每一对的和都为 n 。所以他们的和为 ϕ(n)×n÷2

至于 ϕ(n)(n>2) 为偶数。因为与 n互质的数都是成对出现的,所以显然与 n互质的数为偶数,即 ϕ(n) 为偶数。

性质5

p|np2|n ,则

ϕ(n)=ϕ(np)p

pnp2n,则ϕ(n)=ϕ(np)(p1)

证明
对于第一点
pnp2n ,则证明 nnp 有相同的质因子,只是 p 这一项的指数不同

那么我们可以将其按照欧拉函数的计算式展开,并相除,可得:

ni=1m(11pi)npi=1m(11pi)=nnp=p

对于第二点
pnp2n ,则说明 pnp 互质(因为 p 为素数)

那么根据欧拉函数为积性函数的这个性质即可证得

ϕ(n)=ϕ(np)ϕ(p)=ϕ(np)(p1)

证毕。

: 这个性质广泛用于递推求欧拉函数,也就是筛法求欧拉函数的基础。

性质6

n为一个正整数 ,n=dnϕ(d)

证明
列出

1n,2n,3n,...,nn,

一共 n个分数,再将他们化简.

最简分数ab在上面出现的话,当且仅当 b|ngcd(a,b)=1
那么以b为分母的分数共ϕ(b)个。

分母一共被划分为 dn1份.

所以

n=dnϕ(d)

七、习题

HDU 3501 Calculation 2

题意:给定整数n,求[1,n]中与n 不互质 的数的和,最后mod 1e9+7

看一下上面的性质4证明,我们知道:设t[1,n]中与n互质的数的和

t=n×ϕ(n)2

我们利用欧拉函数和欧几里德定理,if gcd(n,i)==1,则有 gcd(n,n-i)==1 ,可以知道其中一个若为i,则存在一个为ni, 那么二者之和为n  ,这样的一共有ϕ(n)/2对, 故与n互质的所有数的和为 n×ϕ(n)/2 ,那么与n 不互质的数就是

n×(n1)/2n×ϕ(n)/2

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define endl "\n"
const int MOD = 1e9 + 7;

// 求单个数值的欧拉函数值
int phi(int x) {
    int res = x;
    for (int i = 2; i <= x / i; i++) {
        if (x % i == 0) {
            res = res / i * (i - 1);
            while (x % i == 0) x /= i;
        }
    }
    if (x > 1) res = res / x * (x - 1);
    return res;
}

signed main() {
    int n;
    while (cin >> n && n) {
        // 小于n的数字中有多少个数字与n互质
        int ans = n * (n - 1) / 2;          // 高斯公式,(首页+末项)*项数/2
        ans = (ans - phi(n) * n / 2) % MOD; // 扣除掉 phi(n)*n/2
        cout << ans << endl;
    }
}

BZOJ 3884 上帝与集合的正确用法

#include <bits/stdc++.h>
using namespace std;

// https://blog.csdn.net/skywalkert/article/details/43955611
// https://www.cnblogs.com/shuguangzw/p/5697183.html
// 指数循环节问题
// https://blog.csdn.net/acdreamers/article/details/8236942
map<int, int> f;
int pow(int x, int k, int p) {
    int ret = 1;
    while (k) {
        if (k & 1)
            ret = (long long)ret * x % p;
        x = (long long)x * x % p;
        k >>= 1;
    }
    return ret;
}
int phi(int x) {
    int ret = x;
    for (int i = 2; i * i <= x; ++i)
        if (x % i == 0) {
            ret -= ret / i;
            while (x % i == 0)
                x /= i;
        }
    if (x > 1)
        ret -= ret / x;
    return ret;
}
int F(int x) {
    if (f.count(x))
        return f[x];
    int p = phi(x);
    return f[x] = pow(2, F(p) + p, x);
}
int main() {
    int t, n;
    scanf("%d", &t);
    f[1] = 0;
    while (t--) {
        scanf("%d", &n);
        printf("%d\n", F(n));
    }
    return 0;
}
posted @   糖豆爸爸  阅读(428)  评论(0编辑  收藏  举报
编辑推荐:
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
阅读排行:
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· Docker 太简单,K8s 太复杂?w7panel 让容器管理更轻松!
历史上的今天:
2017-09-01 数据遗留问题
2016-09-01 关于教育基础数据改造的一些想法
2015-09-01 分布式系统解决方案
2014-09-01 关于二级缓存的设计思路
2014-09-01 关于Sphinx中使用 RealTime Index的问题
2014-09-01 将Sphinx的日志放置到/dev/shm里需要注意的事情
Live2D
点击右上角即可分享
微信分享提示