乘法逆元
(一)、定义。
对于正整数和,如果有,
那么把这个同余方程中的最小正整数解叫做模的逆元。
个人理解:
在一些题目中,经常因为数据过大,题目要求结果要模除一个数,常见的mod=1e9+7 (素数)
有时候,代码运算过程中会用到除法,而所有数据都已经模除了mod,导致除法可能会失真。
比如在mod=14的情况下,我要求25/5的值(理应是5),但是这里的25早已经不是25,
而是25%mod=11,
这时候得到的值其实是11/5=2,已经不是正确值5了。
这种时候乘法逆元就横空出世了。。
先声明,我要求的 a/b 是在整数范围内进行的,也就是b能整除a,乘法逆元才适用
意义:
上面的例子中,a=25,b=5.a/b=5。而mod=14,a在模除了mod之后再计算a/b的结果是错的。
有逆元的前提是,b和mod是互质的。
想得到正确结果,假设b关于mod的逆元为inv。
那么:
a/b = (a*inv)%mod;(公式,不做证明了)
也就是把a除以一个数,化作了a乘以一个数。
b和inv的关系是 (b*inv)%mod=1;是不是有点像倒数
(二)、求逆元的方法。
1、拓展欧几里得
求b关于mod的逆元。
由于b与mod互质,即最大公约数为1。那么对于 b*x + mod*y = 1
存在一个最小的正整数x,这个x就是b关于mod的逆元
拓展欧几里得个人总结:点击打开链接
求b关于mod的逆元:
#include<stdio.h> int exgcd(int a,int b,int &x,int &y) { if(b==0) { x=1;y=0; return a; } int d=exgcd(b,a%b,y,x); y=y-a/b*x; return d; } int inv(int b,int mod)//求b关于mod的逆元 { int x,y; exgcd(b,mod,x,y); return (x%mod+mod)%mod; } int main() { int b, mod=14; while(~scanf("%d",&b)) { printf("b关于mod的逆元:%d\n",inv(b,mod)); } return 0; }
2、费马小定理
在mod是素数的情况下,对任意整数 b 都有 bp≡b(mod)p
即如果b无法被mod整除,则有 (b的(mod-1)次幂)%mod=1
(b * b的mod-2次幂)%mod=1;
所以b的mod-2次幂%mod,就是b关于mod的逆元
上面这串话,我理解不了。
反正公式就是:
b关于mod的逆元 inv = (b的mod-2次幂)%mod;
求b关于mod的逆元(mod必须是素数才可以)
#include<stdio.h> int qpow(int x,int m,int mod)//快速幂,求x的m次方 { int ans=1; while(m) { if(m%2) ans=(ans*x)%mod; x=(x*x)%mod; m=m/2; } return ans; } int inv(int b,int mod)//求b关于mod的逆元 { return qpow(b,mod-2,mod); } int main() { int b, mod=17; while(~scanf("%d",&b)) { printf("b关于mod的逆元:%d\n",inv(b,mod)); } return 0; }
3、递归求乘法逆元(应用了4、里面的递推公式)
#include<stdio.h> const int mod=17; int inv(int b,int mod) { if(b==1)return 1; return (mod-mod/b)*inv(mod%b,mod)%mod; } int main() { for(int i=1;i<mod;i++) printf("%d关于mod的逆元:%d\n",i,inv(i,mod)); }
4、递推求逆元。求1~mod-1之间所有数的逆元,递推公式。
(1)方法:
初始化1的逆元为1,即inv [ 1 ] = 1 , 正序递推。
inv [ i ] = (mod - mod/i)*inv[ mod%i ] %mod;
#include<stdio.h> int main() { int mod=17, Inv[100]; Inv[1]=1; for(int i=2;i<mod;i++) Inv[i]=(mod-mod/i)*Inv[mod%i]%mod; for(int i=1;i<mod;i++) printf("%d关于mod的逆元:%d\n",i,Inv[i]); }
(2)拓展一种求1~n!之间的数关于mod的逆元的递推(!表示阶乘)
方法:
先求出(mod-1)!的逆元,倒序递推。
inv [ i ] = (inv [ i+1 ]*(i+1))%mod;
原理不会。
#include<stdio.h> int qpow(int x,int m,int mod)//快速幂,求x的m次方 { int ans=1; while(m) { if(m%2) ans=(ans*x)%mod; x=(x*x)%mod; m=m/2; } return ans; } int inv(int b,int mod)//求b关于mod的逆元 { return qpow(b,mod-2,mod); } int fac(int t) { if(t==1)return 1; return t*fac(t-1); } int main() { int mod=17, Inv[100]; Inv[mod-1]=inv(fac(mod-1),mod);//fac表阶乘,可以提前打表 for(int i=mod-2;i>0;i--) Inv[i]=(Inv[i+1]*(i+1))%mod; for(int i=1;i<mod;i++) printf("%d关于mod的逆元:%d\n",fac(i),Inv[i]); }