乘法逆元的研究
介绍
模世界真是奇妙无穷
我们已经很轻松地学会了乘法,加法,减法的取模运算规则。
但是我们却对除法取模毫无办法。
直接模?显然不行,例如:
很明显结果是不一样的。
我们这里理由倒数的思想,定义出了乘法逆元。
定义
对于一个数a,若存在\(a\times b\equiv 1(mod\ p)\)
那么我们将\(b\)记为\(a\)关于模数\(p\)的乘法逆元
那么我们怎么求得这个值呢?
算法
拓展欧几里得算法
拓展欧几里得可以解出\(ax+by=gcd(a,b)\)这个不定方程的特解。
对于一个形如\(ax\equiv b \pmod p\)的式子,我们可以把原式写成\(ax+py=b\)的形式,这是一个二元一次不定方程。
根据裴蜀定理,如果\((a,p)\mid b\),那么这个方程的解为无数个,反之无解。
那么如何解这个方程呢?
我们可以通过拓展欧几里德算法在递归求解\((a,b)\)时顺便求解(以下简称拓欧)。
设\((a,b)=d\)。
显然我们在\(b=0,a\neq0\)时可以得到一组特解为\(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\times \lfloor \frac{a}{b} \rfloor\) 。
我们对原式进行转化,可以转化为\(b x_0+(a-b\times \lfloor \frac{a}{b} \rfloor)y_0=d\)
原式提取出\(a,b\)我们可以化简为\(y_0a+(x_0-\lfloor \frac{a}{b} \rfloor y_0)b=d\)
这样我们就可以进行递归求解了。
当然求出来的仍然是一组特解,但是我们很容易通过等式的性质把它转化为通解:\(atx+bty=t\gcd(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}x\equiv 1\pmod 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;
}