1.扩展欧几里得
求解线性方程 ax≡b(mod m)
对于实数运算下的方程 ax=b 是不是很好解决啊
如果在mod m的运算下,也有ay≡1(mod m) 这样的a的倒数存在,方程就可以求解了
我们把这样的y叫做a的逆元 记为a^-1
为什么要有乘法逆元呢?
当我们要求(a/b) mod p的值,且a很大,无法直接求得a/b的值时,我们就要用到乘法逆元。
我们可以通过求b关于p的乘法逆元k,将a乘上k再模p,即(a*k) mod p。其结果与(a/b) mod p等价。
根据bk≡1 (mod p)有bk=px+1。
k=(px+1)/b。 这里的k就是b的乘法逆元
把k代入(ak) mod p,得:
(a(px+1)/b) mod p
=((apx)/b+a/b) mod p
=[((apx)/b) mod p +(a/b)] mod p
=[(p(ax)/b) mod p +(a/b)] mod p
//p[(a*x)/b] mod p=0
所以原式等于:(a/b) mod p
请别把b^(-1)看成倒数,这会在后面引起思维的混乱,他是逆元!!
记住b^(-1)b%p =1
更简单的证明: a/b mod p = a (bb^(-1) ) /b =ab^(-1);
讲了挺多废话,你还是没告诉我怎么求逆元啊
ax≡1 (mod p)
ax=mp+1
//对吧 此时不含mod p
ax-mp=1
//对吧 此时不含mod p
(ax-mp) mod p = 1 mod p
ax mod p = 1 mod p
//看这里的x就是我们要求的逆元了
但是对于这个问题,a知道,p知道,x是我们要求的逆元,m我不知道,该如何入手呢?
下面的ll代表long long类型
ll extGCD(ll a,ll b, ll &x,ll &y){ //计算ax+by=gcd(a,b) 中x,y的值
if(!b){ //b=0加个特判 ax=gcd(a,b)=a -> x=1
x=1;
y=0;
return a;
}
ll gcd=extGCD(b,a%b,x,y);
ll t=x;
x=y;
y=t-a/b*y;
return gcd;
}
代码看完了,一万个草泥马,你这写的啥玩意啊,第一步我懂,后面讲的什么东西?
梳理一下
gcd(a , b) = gcd(b , a mod b)
没错吧 ->辗转相除法!
a*x+b*y = gcd(a , b)
//嗯? 这是一个丢番图方程
= gcd(b , a mod b)
//没错
= b * x1 + (a mod b) * y1
//他好像倒着用了第一步
= b * x1 + (a - a / b * b) * y1 即 //soga
ax+by = b * x1 + (a - a / b * b) * y1
化简上式,得
ax+by = ay1 - ba/by1 + bx1 , 即
a * x + b * y
= a * y1 + b * (x1-a/b*y1)
x=y1
y=x1 - a/b*y1
上面的思想是以递归定义的,因为 gcd 不断的递归求解一定会有个时候 b=0,所以递归可以结束。回头看看代码,是不是很有趣
以上推论看了别人的思考:知乎
回到求逆元的问题
ax-my=1
如何用扩展欧几里得呢
ll rev(ll a, ll mod){//求a在模mod意义下的乘法逆元
ll x,y; //y是多少并不关心,他只是一个倍数
extGCD(a,mod,x,y); //a*x+mod*y=1 gcd(a,mod),当mod和a互素=1
return (x%mod+mod)%mod;
}
2.组合数
除了求解线性方程,逆元的作用何在
fact[0]=1; //预处理阶乘表
for(int i=1;i