拓展欧几里得算法
拓展欧几里得
-
问题:求方程 \(ax+by=gcd(a,b)\) 中 \(x,y\) 的整数解
-
预备知识:根据某个数学证明,方程 \(ax+by=c\) 有解当且仅当 \(c\equiv0\pmod{gcd(a,b)}\) 。所以只要方程有解,就可以通过求出方程 \(ax+by=gcd(a,b)\) 的解,来求方程 \(ax+by=c\) 的解。
-
拓展欧几里得 \(exgcd\):普通的欧几里得算法就是辗转相除,而拓展欧几里得就是利用了辗转相除的一些性质,提出了一种求不定方程特解的算法。
首先,对于不定方程 \(ax+by=gcd(a,b)\)
假设存在一组解 \((x1,y1)\) 满足
\(bx_1+(a\bmod b)y_1=gcd(b,a\bmod b)\)
由 \(gcd\) 的性质可得,\(gcd(a,b)=gcd(b,a\bmod b)\)
所以 \(ax+by=bx_1+(a\bmod b)y_1\)
\(a\bmod b\) 实际上就是 \(a-a/b \times b\)
于是可以整理出:
\(ax+by=ay_1+b(x_1-(a/b)y_1)\)
由于 \(x1,y1\) 已知,所以就找到了关于 \(x,y\) 的一组解:
\(\begin{cases}x=y_1\\y=x_1-(a/b)y_1\end{cases}\)
然后就结束了。
然而还有一个很重要的问题,
\(x_1,y_1\) 怎么求???
再把前面的两个式子拿过来看看:
\(\begin{cases}ax+by=gcd(a,b)&1\\bx_1+(a\bmod b)y_1=gcd(b,a\bmod b)&2\end{cases}\)
想要求方程1的解,就要先知道方程2的解。
想要知道方程2的解,就要知道与之对应的一个方程3的解。
很明显的递归结构。
可以观察到,每一次方程的系数都在变小,所以递归一定有一个终点—— \(b=0\),类似于求 \(gcd\) 的函数。
而当 \(b=0\) 时,\(gcd(a,b)=gcd(a,0)=a\)
所以只要 \(x\) 取1,\(y\) 取任意值皆可(最好取0)
所以就可以开始回溯,回溯的时候每一次都用上文所提的通解:
\(\begin{cases}x=y_1\\y=x_1-(a/b)y_1\end{cases}\)
然后就真的结束了。。。
例题:P1082 同余方程
代码:大致模板
/*
问题:求不定方程 ax+by==1 的整数解
*/
#include<bits/stdc++.h>
using namespace std;
void exgcd(int a,int b,int &x,int &y){//拓欧
if(b==0){
x=1;y=0;//递归尽头,b==0 的情况
return;
}
exgcd(b,a%b,x,y);//改变系数,继续递归
int tmp=y;
y=x-(a/b)*y;
x=tmp;//代用 x,y x1,y1 的通解公式
}
int gcd(int x,int y){return y==0?x:gcd(y,x%y);}
int main(){
int a,b,x,y;
scanf("%d%d",&a,&b);
if(gcd(a,b)>1){//如果两个数不互质,无解
puts("No solution");
return 0;
}
exgcd(a,b,x,y);
printf("The solution of the problem is x=%d y=%d\n",x,y);
}