介绍
模世界真是奇妙无穷
我们已经很轻松地学会了乘法,加法,减法的取模运算规则。
但是我们却对除法取模毫无办法。
直接模?显然不行,例如:
很明显结果是不一样的。
我们这里理由倒数的思想,定义出了乘法逆元。
定义
对于一个数a,若存在(a imes bequiv 1(mod p))
那么我们将(b)记为(a)关于模数(p)的乘法逆元
那么我们怎么求得这个值呢?
算法
拓展欧几里得算法
拓展欧几里得可以解出(ax+by=gcd(a,b))这个不定方程的特解。
对于一个形如(axequiv b pmod p)的式子,我们可以把原式写成(ax+py=b)的形式,这是一个二元一次不定方程。
根据裴蜀定理,如果((a,p)mid b),那么这个方程的解为无数个,反之无解。
那么如何解这个方程呢?
我们可以通过拓展欧几里德算法在递归求解((a,b))时顺便求解(以下简称拓欧)。
设((a,b)=d)。
显然我们在(b=0,a
eq0)时可以得到一组特解为(x_0=1,y_0=0),满足(bx_0+(b\%a)y_0=d)
在回溯的时候,我们就有了(b x_0+(b \% a)y_0=d)的解,如何利用这个解求出(ax+by=d)的解呢?
对于一个取模,我们有(a\%b=a-b imes lfloor frac{a}{b}
floor) 。
我们对原式进行转化,可以转化为(b x_0+(a-b imes lfloor frac{a}{b}
floor)y_0=d)
原式提取出(a,b)我们可以化简为(y_0a+(x_0-lfloor frac{a}{b}
floor y_0)b=d)
这样我们就可以进行递归求解了。
当然求出来的仍然是一组特解,但是我们很容易通过等式的性质把它转化为通解:(atx+bty=tgcd(a,b))
我们通过算数变换就能把它调整到任意([p,p+gcd(a,b)])范围内。
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,x,y),t=x;
x=y;y=t-(a/b)*y;
}
我们发现如果在同余方程(x^{-1}xequiv 1pmod p)中,(x^{-1})就是(x)在(mod p)意义下的乘法逆元。
那么我们可以通过求解这个同余方程解出这个(x^{-1})。
费马小定理
利用费马小定理,我们有(a^{p-1}equiv 1(mod p))
那么a的乘法逆元就是(a^{p-2})。
注意这里p必须是质数,并且a,p互质。
欧拉定理以及数论阶
当p不保证为质数的时候,我们可以用欧拉定理以及数论阶求出a最小的逆元。
代码留坑
线性求逆元
上面都是log的做法,但是当我们要求大量逆元时(比如求组合数)。
log的单次询问做法就不怎么管用了。
我们需要一种更加高效的算法,支持(O(n))预处理,(O(1))查询。
假设我们已经拥有了(1~i-1)关于模数p的(以下简称逆元)
那么我们设
那么在模p意义下
我们给两边同乘(i^{-1}*r^{-1})得
移项得
由于(r=p\%i),(k=p/i)我们得到
这两个我们都已经得到了
我们就很轻松递推出(i)的逆元了。
注意这里 $ -(p/i)*(p%i)^{-1}$为一个负数,我们在计算的时候要把它转化为正数。
int init(int n){
inv[0]=inv[1]=1;
for(int i=2;i<=n;i++)inv[i]=(p-p/i)*inv[p%i]%p;
}