一.定义
(及如何理解)
如果a*x≡1 (mod p),且gcd(a,p)=1(a与p互质),则称a关于模p的乘法逆元为x。(from Wikipedia)
a*x≡1 (mod p) 表示 a乘一个数x并模p等于1,即 a*x%p=1;看上去就是同余定理的一个简单等式。
而x 为 a 的逆元,记为x=a-1,所以我们也可以称 x 为 a 在mod b意义下的倒数,
什么意思呢? 可以理解为在x的倒数上加了个限定:
倒数定义为a*x=1,则x为a的倒数;
而逆元为:a*x%p=1;
用处:一般用于求 a/b (mod p) 的值(p 通常为质数),是解决模意义下分数数值的必要手段。
(比如某道水题让你对结果%p,而计算过程中有除法运算。。)
二.求解
1.扩展欧几里得算法求解逆元
我们知道,a*x≡1 (mod p) 就是 a*x%p=1 也就是 a*x+p*y=1
而扩展欧几里得算法就是用来求线性同余方程a?x≡c ( mod b ),只不过此时c=1;
#include<cstdio> #include<cmath> #define R register int #define ll long long using namespace std; int n,p; void ex_gcd(ll a,ll b,ll& x,ll& y) { if(!b) x=1,y=0; else ex_gcd(b,a%b,y,x),y-=a/b*x; } int main() { scanf("%d%d",&n,&p); for(R i=1;i<=n;i++) { ll x,y; ex_gcd(a,p,x,y); x=(x%p+p)%p; printf("%d\n",x); } return 0; }
2.费马小定理和快速幂求解逆元
由费马小定理可知:ap-1≡1 (mod p)
所以a*ap-2≡1 (mod p)
即ap-2就是a在mod p意义下的逆元
#include<cstdio> #include<iostream> #include<cmath> #define R register int #define ll long long using namespace std; int n,p; ll q_pow(ll x,ll ind,ll mod) { x%=mod; ll a=1; for(;ind;ind>>=1,(x*=x)%=mod) if(ind&1) (a*=x)%=mod; return a; } int main() { scanf("%d%d",&n,&p); for(R i=1;i<=n;i++) printf("%lld\n",q_pow(i,p-2,p)); return 0; }
3.O(n)的线性算法
原题:P3811 【模板】乘法逆元 https://www.luogu.org/problemnew/show/P3811
求 [1,n] 区间中每个数的逆元,其实这是一种递推。。。
我们设 inv[i] 表示i的逆元,设 p=k*i+r (1<r<i<p) 可得:k*i+r≡0 (mod p) (显然)
则左右同乘 r-1 * i-1 :k*r-1+i-1≡0 (mod p)
所以:i-1≡ -k*r-1 (mod p);
易知:r-1=inv[p%i],k=p/i
所以:i-1 = p/i * inv[p%i]
而由逆元定义可知:inv[1]=1 (mod p),你就可以开始递推了。
#include<cstdio> #include<iostream> #define R register int using namespace std; int n,p; int inv[3000010]; int main() { scanf("%d%d",&n,&p); inv[1]=1;printf("1\n"); if(p>=2) for(R i=2;i<=n;i++) inv[i]=(long long)(p-p/i)*inv[p%i]%p,printf("%d\n",inv[i]);//话说记得用long long寄存,要不就死了。。。 return 0; }
4.阶乘逆元
。。。还未学会。。。安利一发zjp_shadow大佬的blog:https://www.cnblogs.com/zjp-shadow/p/7773566.html (写得太好了)
原文地址:https://www.cnblogs.com/Jackpei/p/10358563.html