逆元 x
逆元:
1.首先定义:
若存在正整数a,x,m,且满足ax≡1(mod m),则称a是x的乘法逆元,或称x是a的乘法逆元.
Eg:
模7意义下,3的乘法逆元是5(或模7意义下,5的乘法逆元是3)
1)3*1%7=3%7=3 =/=1(x)
2)3*2%7=6%7=6 =/=1(x)
3)3*3%7=9%7=2 =/=1(x)
4)3*4%7=12%7=5 =/=1(x)
5)3*5%7=15%7=1 ==1 (√)
||
||
||
v
模7意义下,3的乘法逆元是5(或模7意义下,5的乘法逆元是3)
其他的求乘法逆元的方式与此类似。
2.乘法逆元存在性定理
我们来考虑一下同余方程:
ax ≡ 1(mod m)
若a 与m 互质, 则一定存在一个正整数解x, 满足x < m.
若a 与m 不互质, 则一定不存在正整数解x.
意思也就是说, 互质与乘法逆元存在互为充要条件.
3.求法
①欧拉定理/费马小定理
- 3.1欧拉定理
- 3.1.1欧拉定理公式:
aφ(p) ≡ 1(mod p)
(是对于任意互质的a,p恒成立的)
-
- 3.1.2推论
a*aφ(p)-1 ≡ 1(mod p)
只有通过这个公式+快速幂才能够求逆元~
-
- 3.1.3欧拉函数定义
欧拉函数φ(n)表示小于等于n且与n互质的正整数的个数
Eg:φ(6)=2,φ(7)=6,φ(1)=1
欧拉函数是一个数论函数, 也是一个积性函数, 可以线性筛出.
- 3.1.4欧拉函数公式:
若令n=∏ki=1 pi ci为n的质因子分解形式,则有
k pi-1
φ(n)=n∏ ----------
i=1 pi
欧拉函数可以利用容斥原理在O(sqrt(n))的时间复杂度上界中求出
②线性求逆元
③拓展欧几里得
逆元一般用拓展欧几里得算法来求得,如果为素数(常用素数有:998244353,1000000007),则经常根据费马小定理(用于降低题目的难度)得到逆元为
其推导过程如下:
例:
求如下表达式的值(已知)(|为整除号)
当然这个经典的问题有很多方法,最常见的就是扩展欧几里得,如果是素数,还可以用费马小定理!!!
但是你会发现费马小定理和扩展欧几里得算法求逆元是有局限性的,它们都会要求与互素。实际上我们还有一种通用的求逆元方法,适合所有情况。
公式如下:
现在我们来证明它,已知,证明步骤如下
而对于(a/b)%m== 一个数
1.当m是素数的时候,根据费马小定理(不懂的可以去这儿看看),直接输出b^(n-2)即可
2.否则,就根据拓展欧几里得exgcd(b,m,x,y)
Ps:拓展欧几里得能够保证求出的x,y满足|x|+|y|最小
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> using namespace std; int a,b,m; int x,y; int exgcd(int a,int b,int &x,int &y) { if(b==0) { x=1; y=0; return a; } int r=exgcd(b,a%b,x,y),tmp; tmp=x,x=y; y=tmp-a/b*y; return r; } int fastpow(int a,int p) { int bb=a;int ans=1; while(p!=0) { if(p%2==1)ans=ans*bb; bb=bb*bb; p=p/2; } return ans; } int main() { scanf("%d%d%d",&a,&b,&m); for(int i=1;i<=sqrt(m);i++) { if(m%i==0) { int ans=exgcd(b,m,x,y); printf("%d",(a*ans)%m); return 0; } } printf("%d",fastpow(b,m-2)); }