扩展欧几里得算法
阅读前须知:本文中的 \(\%\) 符号均为取模运算(主要是由于计算机语言中往往这样表示);若 \(ax+by=k\) 且 \(x,y\in\mathbb{Z}\) 则称 \(k\) 为 \(a,b\) 的线性组合
欧几里得算法
欧几里得算法就是大家以前学过的辗转相除法,可以用来计算两个数字的最大公约数(\(\gcd\)):
\(\gcd(a,b)=\gcd(b,a\%b)\)
证明
对于 \(a,b\ (a\ge b)\) 不妨设 \(a=kb+r\)
若 \(r=0\) 则说明 \(b|a\) ,所以 \(\gcd(a,b)=b\)
若 \(r\ne0\) 则对于任意 \(t|a\) 且 \(t|b\) ,都有 \(t|kb\) ,又因为 \(a-kb=r\) ,所以也有 \(t|r\) ,同理,对于任意 \(t|r\) 且 \(t|b\) ,都有 \(t|kb\) ,又因为 \(r+kb=a\) ,所以也有 \(a|r\) ,所以 \(a,b\) 的所有公因数也就是 \(b,r\) 的所有公因数
c++ code
int gcd(int a,int b)
{
return b?gcd(b,a%b):a;
}
裴蜀定理(贝祖定理)
若 \(a,b\in\mathbb{Z}\) ,当且仅当 \(\gcd(a,b)|m\) 时关于 \(x,y\) 的方程 \(ax+by=m\) 有整数解,同时一定存在一组 \(x,y\in\mathbb{Z}\) 使得 \(ax+by=\gcd(a,b)\) 成立
我们称 \(ax+by=m\) 这个等式为裴蜀等式,其有解时必然有无穷多个解,每一组解 \(x,y\) 被称为裴蜀数,可以使用拓展欧几里得算法来求解裴蜀数
证明
设 \(s=ax+by\) 且为 \(a,b\) 能线性组合出的最小正数,令 \(a=qs+r\)
则 \(r=a-qs=a-q(ax+by)=a-qxa-qyb=(1-qx)a-qyb\)
即 \(r=(1-qx)a+(-qy)b\),所以 \(r\) 也是 \(a,b\) 的线性组合
因为 \(r=a\%s\),所以 \(r\in[0,s)\)
由 \(s\) 的定义,\(s\)为 \(a,b\) 能线性组合出的最小正数,\(r\) 也是 \(a,b\) 的线性组合且 \(r\in[0,s)\),所以 \(r=0\)
故 \(s|a\),同理可得 \(s|b\)
故 \(s\) 为 \(a,b\) 的公约数,故 \(\gcd(a,b)\ge s\)
因为 \(\gcd(a,b)|ax\),\(\gcd(a,b)|by\),\(\gcd(a,b)|ax+by\),所以 \(\gcd(a,b)|s\)
所以 \(s\ge\gcd(a,b)\)
又因为 \(s\le\gcd(a,b)\)
所以 \(s=\gcd(a,b)=ax+by\)
所以 \(\gcd(a,b)\) 是 \(a,b\) 能线性组合出的最小正数,等式两边同时乘以一个整数即可推广到裴蜀定理
PS:在下方我们使用扩展欧几里得算法也能证明裴蜀定理
扩展欧几里得算法
扩展欧几里得算法就是利用欧几里得算法原理推导出的求解裴蜀等式的解的算法
推导
对于 \(ax+by=\gcd(a,b)\)
当 \(b=0\) 时,显然 \(x=1,y=0\) 满足等式
当 \(b>0\) 时,不妨设有 \(ax_1+by_1=\gcd(a,b)\) 和 \(bx_2+(a\%b)y_2=\gcd(b,a\%b)\)
根据欧几里得算法的原理 \(\gcd(a,b)=\gcd(b,a\%b)\) ,可以得出
可以将 \(a\%b\) 展开为 \(a-b\lfloor\frac{a}{b}\rfloor\) ,原式就可以变形为
整理得
根据多项式恒等定理可以得出
这样就知道了求解 \(x_1,y_1\) 的方法:通过迭代(递归),在 \(x_2,y_2\) 的基础上算出 \(x_1,y_1\) ,当迭代到 \(b=0\) 时,显然 \(\gcd(a,b)=a\),所以有 \(x=1,y=0\) ,迭代结束
推广——证明裴蜀定理
由扩展欧几里得算法,我们一定能够构造出一组 \(x,y\in\mathbb{Z}\) 使得 \(ax+by=\gcd(a,b)\) 成立
那么将等式两边同时乘一个整数就证明了裴蜀定理
c++ code
int exgcd(int a,int b,int &x1,int &y1)
{
if(b==0)//递归边界
{
x1=1,y1=0;
return a;
}
int x2,y2;
int r=exgcd(b,a%b,x2,y2);//递归
x1=y2,y1=x2-(a/b)*y2;//使用结论
return r;
}
这样写虽然比较清晰明了,但是效率不高,我们可以稍微优化一下
int exgcd(int a,int b,int &x,int &y)
{
if(!b) return x=1,y=0,a;
int r=exgcd(b,a%b,y,x);
y-=(a/b)*x;
return r;
}
补充
我们上面的方法只算出了 \(ax+by=\gcd(a,b)\) 的一组特解,对于更一般的方程 \(ax+by=c\) ,它有解当且仅当 \(\gcd(a,b)|c\) ,我们把算出的特解都乘以 \(\frac{c}{\gcd(a,b)}\) 就是 \(ax+by=c\) 的一组特解
那么怎么将这一组特解推广到通解呢?
对于方程 \(ax+by=\gcd(a,b)\) ,考虑把方程中的 \(x\) 加上一个数 \(k_1\),把 \(y\) 减去一个数 \(k_2\),只要保证原方程仍成立那么 \(x+k_1,y-k_2\) 就是一组新的解
把方程写出
拆开括号并整理
可以看出满足 \(ak_1=bk_2\) 时原方程仍成立,发现 \(ak_1,bk_2\) 都一定是 \(a,b\) 的公倍数,所以有
因此寻找一组新的解只要将 \(x\) 加上 \(\frac{b}{\gcd(a,b)}\) ,将 \(y\) 减去 \(\frac{a}{\gcd(a,b)}\) 就可以了
综上,方程 \(ax+by=c\) 的通解可以表示为:
在实际应用时,为了得到最小正整数解,可以让 \(x=\left(\left(\frac{c}{\gcd(a,b)}x_0\right)\%\frac{b}{\gcd(a,b)}+\frac{b}{\gcd(a,b)}\right)\%\frac{b}{\gcd(a,b)}\) ,对于 \(y\) 同理
应用
求乘法逆元
若 \(ax\equiv 1\pmod p\) 其中 \(\gcd(a,p)=1\),那么我们就称 \(x\) 为 \(a\) 在模 \(p\) 意义下的乘法逆元
求 \(ax\equiv 1\pmod p\) 的解就相当于求 \(ax+py=1\) 的解,使用扩欧即可,最后的结果模 \(p\) 就可以算出 \(a\) 在模 \(p\) 意义下的乘法逆元
UPD:
2022.8.8 修正了一些错误,润色了部分语言,补充了裴蜀定理不需要扩展欧几里得算法的证明
2022.8.9 优化了扩展欧几里得算法的c++代码