乘法逆元的 exgcd 求法
乘法逆元
对于一个整数 a a a,它的逆元 d d d 等于 a − 1 a^{-1} a−1。
显然有 a d = 1 ad=1 ad=1。
在 OI 中,大部分情况都是对一个整数 p p p 进行取模的情况下求逆元。
即求出满足 a d ≡ 1 ( m o d p ) ad\equiv 1(\bmod p) ad≡1(modp) 的 d d d。
费马小定理求法与扩展欧几里得求法
费马小定理求法
这种求法仅限于 p p p 是一个质数的情况下。
费马小定理:对于质数 p p p 和任意整数 a a a,有 a p − 1 ≡ 1 ( m o d p ) a^{p-1}\equiv 1(\bmod p) ap−1≡1(modp)。
所以令 d = a p − 2 d=a^{p-2} d=ap−2,一定有 a d = a × a p − 2 = a p − 1 ≡ 1 ( m o d p ) ad=a\times a^{p-2}=a^{p-1}\equiv 1(\bmod p) ad=a×ap−2=ap−1≡1(modp)。
这里用快速幂用 log 的时间随便求求就好了。本文的重点不在这里。
扩展欧几里得
扩展欧几里得,简称 exgcd。用到了普通 gcd 的性质。
gcd 的性质: gcd ( a , b ) = gcd ( b , a m o d p ) \gcd(a,b)=\gcd(b,a\bmod p) gcd(a,b)=gcd(b,amodp)。
扩展欧几里得算法是求解类似于给定 a , b a,b a,b,求 a x + b y = gcd ( a , b ) ax+by=\gcd(a,b) ax+by=gcd(a,b) 的一组可行解 ( x , y ) (x,y) (x,y)。
先来看看这个式子与乘法逆元有什么关系。
a x ≡ 1 ( m o d p ) a x − 1 ≡ 0 ( m o d p ) ax\equiv 1(\bmod p)\\ ax-1\equiv 0(\bmod p)\\ ax≡1(modp)ax−1≡0(modp)
所以我们设 a x − 1 = k p ax-1=kp ax−1=kp。其中 k k k 是一个整数。
那么式子又变成了:
a x − 1 = k p a x − k p = 1 ax-1=kp\\ ax-kp=1 ax−1=kpax−kp=1
我们令 b = p , y = − k b=p,y=-k b=p,y=−k,就可以转化为 a x + b y = 1 ax+by=1 ax+by=1 的情况了。
所以我们只要解出 a x − k p = 1 ax-kp=1 ax−kp=1 这个方程,就得到了 x x x。它就是我们所求的答案。
注意其实 y y y 并不重要。
然后我们讨论如何求类似于这一类的方程。
根据上文的公式 gcd ( a , b ) = gcd ( b , a m o d b ) \gcd(a,b)=\gcd(b,a\bmod b) gcd(a,b)=gcd(b,amodb),如果我们知道了在 gcd ( b , a m o d b ) \gcd(b,a\bmod b) gcd(b,amodb) 情况下的解 ( x ′ , y ′ ) (x',y') (x′,y′),就显然有 b x ′ + ( a m o d b ) y ′ = 1 bx'+(a\bmod b)y'=1 bx′+(amodb)y′=1。
而 a m o d b a\bmod b amodb 又可以转化为 a − ⌊ a b ⌋ b a-\lfloor\frac{a}{b}\rfloor b a−⌊ba⌋b。
所以原式又有:
b x ′ + ( a − ⌊ a b ⌋ b ) y ′ = 1 b x ′ + a y ′ − ⌊ a b ⌋ b y ′ = 1 a y ′ + b ( x ′ + ⌊ a b ⌋ y ′ ) = 1 bx'+(a-\lfloor\frac{a}{b}\rfloor b)y'=1\\ bx'+ay'-\lfloor\frac{a}{b}\rfloor by'=1\\ ay'+b(x'+\lfloor\frac{a}{b}\rfloor y')=1 bx′+(a−⌊ba⌋b)y′=1bx′+ay′−⌊ba⌋by′=1ay′+b(x′+⌊ba⌋y′)=1
所以可以得出 x = y ′ , y = x ′ + ⌊ a b ⌋ y ′ x=y',y=x'+\lfloor\frac{a}{b}\rfloor y' x=y′,y=x′+⌊ba⌋y′。
于是就可以递归下去求答案了。
那么对于递归的尽头,即当 b = 0 b=0 b=0 时, a x + 0 y = gcd ( a , 0 ) = a ax+0y=\gcd(a,0)=a ax+0y=gcd(a,0)=a 时,令 x = 1 x=1 x=1, y y y 取任何恰当的值即可。别整什么 2147483647 2147483647 2147483647 之类的作死行为就好。
哦对, y y y 建议是个非负数,比如 0 0 0、 1 1 1 之类的都挺不错。如果你取个 − 1 -1 −1 之类的,最后算出来可能要多次取模,十分麻烦。
例题
代码:
cpp
#include<bits/stdc++.h>
using namespace std;
typedef long long ljl;
ljl a,b,ans,t;//t就是来打酱油的
void exgcd(ljl a,ljl b,ljl &x,ljl &y)
{
if(!b)
{
x=1;y=0;
return;
}
exgcd(b,a%b,x,y);
ljl tx=x;
x=y;y=tx-a/b*y;
return;
}
int main(){
ios::sync_with_stdio(0);
cin>>a>>b;
exgcd(a,b,ans,t);
cout<<(ans+b)%b<<'\n';//这里注意的ans可能是个负数,要取模
return 0;
}