拓展欧几里得
-
问题:求方程 (ax+by=gcd(a,b)) 中 (x,y) 的整数解
-
预备知识:根据某个数学证明,方程 (ax+by=c) 有解当且仅当 (cequiv0pmod{gcd(a,b)}) 。所以只要方程有解,就可以通过求出方程 (ax+by=gcd(a,b)) 的解,来求方程 (ax+by=c) 的解。
-
拓展欧几里得 (exgcd):普通的欧几里得算法就是辗转相除,而拓展欧几里得就是利用了辗转相除的一些性质,提出了一种求不定方程特解的算法。
首先,对于不定方程 (ax+by=gcd(a,b))
假设存在一组解 ((x1,y1)) 满足
(bx_1+(amod b)y_1=gcd(b,amod b))
由 (gcd) 的性质可得,(gcd(a,b)=gcd(b,amod b))
所以 (ax+by=bx_1+(amod b)y_1)
(amod b) 实际上就是 (a-a/b imes b)
于是可以整理出:
(ax+by=ay_1+b(x_1-(a/b)y_1))
由于 (x1,y1) 已知,所以就找到了关于 (x,y) 的一组解:
(egin{cases}x=y_1\y=x_1-(a/b)y_1end{cases})
然后就结束了。
然而还有一个很重要的问题,
(x_1,y_1) 怎么求???
再把前面的两个式子拿过来看看:
(egin{cases}ax+by=gcd(a,b)&1\bx_1+(amod b)y_1=gcd(b,amod b)&2end{cases})
想要求方程1的解,就要先知道方程2的解。
想要知道方程2的解,就要知道与之对应的一个方程3的解。
很明显的递归结构。
可以观察到,每一次方程的系数都在变小,所以递归一定有一个终点—— (b=0),类似于求 (gcd) 的函数。
而当 (b=0) 时,(gcd(a,b)=gcd(a,0)=a)
所以只要 (x) 取1,(y) 取任意值皆可(最好取0)
所以就可以开始回溯,回溯的时候每一次都用上文所提的通解:
(egin{cases}x=y_1\y=x_1-(a/b)y_1end{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
",x,y);
}