扩展欧几里得算法的最大用法就是用来计算二元一次方程的整数解。
**贝祖定理:**即如果a、b是整数,那么一定存在整数x、y使得ax+by=gcd(a,b)。
或者说二元一次方程有解的充要条件就是二元一次方程(ax + by = c)有解的充要条件就是 c 是 gcd(a, b) 的整数倍
这时候使用扩展欧几里得就可以求得二元一次方程的一个解。
当到达递归边界的时候,b==0,a=gcd(a,b) 这时可以观察出来这个式子的一个解:a1+b0=gcd(a,b),x=1,y=0,注意这时的a和b已经不是最开始的那个a和b了,所以我们如果想要求出解x和y,就要回到最开始的模样。
初步想法:由于是递归的算法,如果我们知道了这一层和上一层的关系,一层一层推下去,就可以推到最开始的。类似数学上的数学归纳法。
假设当前我们在求的时a和b的最大公约数,而我们已经求出了下一个状态:b和a%b的最大公因数,并且求出了一组x1和y1使得 b*x1+(a%b)*y1=gcd
(注意在递归算法中,永远都是先得到下面一个状态的值)
这时我们可以试着去寻找这两个相邻状态的关系:
首先我们知道:a%b=a-(a/b)*b;带入:
b*x1 + (a-(a/b)*b)*y1
= bx1 + ay1 – (a/b)by1
= ay1 + b(x1 – a/by1) = gcd 发现 x = y1 , y = x1 – a/by1
这样我们就得到了每两个相邻状态的x和y的转化,就可以在求gcd的同时对x和y进行求值了。
板子:
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int exgcd(int a,int b,int &x,int &y)//扩展欧几里得算法
{
if(b==0)
{
x=1;y=0;
return a; //到达递归边界开始向上一层返回
}
int r=exgcd(b,a%b,x,y);
int temp=y; //把x y变成上一层的
y=x-(a/b)*y;
x=temp;
return r; //得到a b的最大公因数
}
这里还有一个裸题用来应用扩展欧几里得算法:
http://poj.org/problem?id=1061
题目意思理解清楚了之后,可以看出根据题目意思列出一个二元一次方程(m + n)a + Lb = y-x。
//扩展欧几里得
#include<iostream>
#include<cstring>
#include<math.h>
#include<algorithm>
#include<stdio.h>
using namespace std;
typedef long long ll;
ll ex_gcd(ll a, ll b, ll &x, ll &y){
if(!b){
x = 1;
y = 0;
return a;
}
ll d = ex_gcd(b, a%b, x, y);
ll t = x;
x = y;
y = t - (a/b) * y;
return d;
}
void solve(ll a, ll b, ll c){
ll x, y;
ll d = ex_gcd(a, b, x, y); // d是 a,b最大公约数
if( c % d ){ //无解情况
cout << "Impossible" << endl;
return ;
}
x = x * c / d; // 求一个解 因为你之前求的 gcd(a,b)的解,所以现在需要乘c。
ll t = b / d;
if(t < 0)
t = -t;
x = (x%t + t) % t;
cout << x << endl;
return ;
}
int main(){
int x, y, m, n, L;
while(cin >> x >> y >> m >> n >> L){
solve(m-n, L, y-x);
}
return 0;
}