[数论第二节]欧拉函数/快速幂/扩展欧几里得算法
-
欧拉函数
- 欧拉函数
: 1-N中与N互质的数的个数 - 若
其中p为N的所有质因子 - 则
-
证明:
- 互质:两数的公共因子只有1
- 去掉所有与N有(大于1的)公共因子的数,剩下的数就是与N互质的数
- 对N的所有质因子
,去掉所有 (与N有公共因子的数), 个数为 ,即 - 对于数
,在去掉 的倍数和去掉 的倍数的过程中去除了两次,所以要加上一次,即 - 对于数
,在去掉 的倍数的过程中被去掉了三次,在加上合数 的过程中被加上了三次,所以合数 没有被去掉,因此要去掉它,即 - 对于合数
同理,归纳递推可知,所有质数个数为: - 同样基于容斥原理:
-
-
- 与N有(大于1的)公共因子的个数 =|
的倍数的个数| - | 的倍数的个数| + | 的倍数的个数 ··· | 的倍数的个数|
-
- 将上式因式分解后:
-
容斥原理:
int res = a; for(int i = 2; i <= a / i; ++ i){ if(a % i == 0){//i为质因子 res = res / i * (i - 1);//套公式 while(a % i == 0) a /= i;//把因子除干净 } } if(a > 1) res = res / a * (a - 1);//最后一个因子可能大于sqrt(a)
-
筛选法:
- 利用线性筛选质数的过程求出每个数的欧拉函数
- 欧拉函数为积性函数,当a与b互质时有
- 当i为质数时,
- 当
时, 为i的质因子,此时 的质因子与i的质因子完全相同,所以 - 当
时, 不为i的质因子,此时 的质因子比i的质因子多一个 ,所以 - 代码:
const int N = 1e6 + 10; typedef long long LL; int primes[N], cnt;//质数数组,下标 int st[N];//标记为合数 int phi[N];//欧拉函数 int n; LL get_eulers(int n){ phi[1] = 1; for(int i = 2; i <= n; ++ i){ if(!st[i]){ primes[ ++ cnt] = i; phi[i] = i - 1;//质数i的欧拉函数为i - 1 } for(int j = 1; primes[j] <= n / i; ++ j){ int pj = primes[j]; st[pj * i] = 1; if(i % pj == 0){//i是合数 phi[pj * i] = pj * phi[i];//pj为i的质因子 break; } else phi[pj * i] = (pj - 1) * phi[i];//pj不是i的因子 } } LL ans = 0; for(int i = 1; i <= n; ++ i) ans += phi[i]; return ans; }
-
欧拉函数的应用
-
欧拉定理
- 若a与n互质,则
- 证明:
- 若a与b互质,且
则 也与b互质 - 设
为1~n中的所有与n互质的数 - 每个数同时乘上a得
由于a也与n互质,所以乘a后所得的数也全部与n互质 - 将每个数mod n,取模后的每个数都在1~n范围内,并且仍然都与n互质,显然取模后的这几个数就是原来的几个数,只不过数的顺序可能发生了变化
- 将所有数相乘,则有
- 所以有:
即 - 特别地,当n为质数时,
为费马定理
- 若a与b互质,且
- 若a与n互质,则
-
- 欧拉函数
-
快速幂
- 快速地求出
,时间复杂度为 - 将k拆分为二进制相加,即
-
- 其中
是k的二进制的每一位(0/1),n最大为k的二进制的位数 - 所以
-
- 所以每次遍历k的所有二进制位,同时预处理出
,根据k的二进制的取值将答案累乘 - 代码:
typedef long long LL; //求a^k mod b int qmi(int a, int k, int b){ int res = 1; while(k){ if(k & 1) res = (LL)res * a % b;//k的当前位非0,则将a累乘到答案 k >>= 1;//k右移一位 a = (LL)a * a % b;//a的幂倍增 } return res; }
-
快速幂求逆元
- 若b与p互质,且
, ,则x为b的逆元 - 两边乘b有
- 所以有
- 若p是质数,有费马定理
则 - 若p不是质数,有欧拉定理
则 - 代码:
int qmi(int a, int b, int p){ 略... } if(b与q互质){ //互质是有解的前提 if(p为质数) cout << qmi(b, p - 2, p); else cout << qmi(b, phi[p] - 1, p); }else 无解
- 若b与p互质,且
- 快速地求出
-
扩展欧几里得定理
-
裴蜀定理:对于任意正整数a,b, 都一定存在整数x,y, 使得
, 并且 一定是a与b能构造出来的最小公约数 -
扩展欧几里得算法就是求这样的x,y
-
用于求解方程
的解 -
当
时 故而 -
当
时,因为 -
-
而
-
-
-
-
故而
-
代码:
int exgcd(int a, int b, int &x, int &y){ if(!b){ //b = 0时,ax + by = gcd(a, 0) = a,所以x = 1, y = 0; x = 1, y = 0; return a; }else { int d = exgcd(b, a % b, x, y);//交换a与b,x与y int t = x; x = y;//x1 = y2 y = t - a / b * y;//y1 = x2 - a / b * y2 return d; } } //简化版 void exgcd(int a, int b, int &x, int &y){ if(!b){ x = 1, y = 0; return a; }else{ int d = exgcd(b, a % b, y, x); y -= a / b * x; return d; } }
-
扩展欧几里得求解线性同余方程
- 线性同余方程:
其中a,x,b,m均为整数,x为未知数,方程等价于 ,也等价于 其中x,k均为未知数 - 方程形如:
的直线方程,解(x,y)为直线上散列的点 - 求出a,b的最大公约数
,方程化为 易知其中 为整数,所以要求 ,故 - 所以线性同余方程
有解的充要条件为b为 的倍数 - 当
时( 为整数),方程无解 - 当
时,先用扩展欧几里得求出方程 的特解 ,可知 方程的解为 ,所以原式的解为 - 代码:
const int N = 1e5 + 10; typedef long long LL; int n; int a, b, m; int x, y; //扩展欧几里得算法 int exgcd(int a, int b , int &x, int &y){ if(!b){ x = 1, y = 0; return a; }else{ int t = exgcd(b, a % b, y, x); y -= a / b * x; return t; } } int main(){ cin >> n; while(n -- ){ cin >> a >> b >> m; int t = exgcd(a, m, x, y); //b是gcd(a,m)的倍数才有解 if(b % t != 0) cout << "impossible" << endl; else cout << (LL)x * (b / t) % m << endl;//特解乘上倍数 } return 0; }
- 线性同余方程:
-
-
中国剩余定理
- 中国剩余定理 (Chinese Remainder Theorem, CRT) 可求解如下形式的一元线性同余方程组(其中
两两互质): -
- 过程:
- 计算所有模数的积
- 对于第
个方程:- 计算
- 计算
在模 意义下的逆元 - 计算
(不要对 取模)
- 计算
- 方程组在模
意义下的唯一解为:
-
扩展版中国剩余定理:
- 当不满足
两两互质时,求解线性同余方程组 -
-
- 由①②知
- 移项
- 其中
已知, 未知,可以用扩展欧几里得算法求出 - 当
时无解 - 当
时,先求出方程 -
- 的特解,求出
后 -
- 此时
就是原方程的特解 - 观察原方程可知通解为
-
- 将通解代入①式得
-
- 所以
-
- 其中
已知, 未知 - 令
所以③式化为 -
- 所以将①②式合并后的③式仍与方程组方程相似,于是可以循环将方程组合并为一个式子,通过最后一个式子即可用扩展欧几里得算法求出x
- 代码:
const int N = 30; typedef long long LL; int n; LL exgcd(LL a, LL b, LL &x, LL &y){ if(!b){ x = 1, y = 0; return a; } LL d = exgcd(b, a % b, y, x); y -= a / b * x; return d; } int main(){ cin >> n; LL a1, m1; bool flag = true; cin >> a1 >> m1;//用于存储更新的a与m for(int i = 1; i < n; ++ i){ //合并n-1次 LL an, mn;//接受新的a与m LL k1, k2; //存储用欧几里得求的系数 cin >> an >> mn; LL d = exgcd(a1, an, k1, k2); //求出gcd(a1, an)以及系数k1, k2 if((mn - m1) % d){ //无解判断 flag = false; break; } LL t = an / d; //通解k1 = k1 + k * (an / d), k2 = k2 + k * (a1 / d),题目为了求最小的x,故要让k1为最小解故要k1不断膜上t k1 = k1 * (mn - m1) / d; //更新k1 k1 = (k1 % t + t) % t; //取k1的最小解 m1 = k1 * a1 + m1; //m更新为k1 * a1 * m1 a1 = abs(a1 / d * an); //a1更新为gcd(a1, an) } if(flag){ cout << (m1 % a1 + a1) % a1; //最后一个式子的就是余数m }else cout << -1; return 0; }
- 当不满足
- 中国剩余定理 (Chinese Remainder Theorem, CRT) 可求解如下形式的一元线性同余方程组(其中
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】