拓展欧几里得是基于欧几里得算法的,在数论中算比较基础的一块了,那么今天就来讲一讲它。
前置技能
-
欧几里得算法
就是GCD,辗转相除法求最大公约数,数论中没有比这个更简单的了。很多人都会想到,就像高斯消元一样,这么叫不过是因为他们出生比较早,当然,死的也比较早。
如果还有不懂,自行百度。
-
裴蜀定理
裴蜀定理是一个关于最大公约数的定理,说明了对任何整数a、b和它们的最大公约数d,关于未知数x和y的线性丢番图方程
看不懂就当我没说,大概意思如下:ax + by = m
有解当且仅当m是d的倍数 (d为a、b最大公约数总看得懂了吧)
基本事实
所求
拓展欧几里得求的是:已知 ax + by = m
,求一组整数解(x,y)(a,b,c均为整数 )。
简化
由裴蜀定理可得:令 ax + by = m
简化为 ax + by = c
(反正m为c倍数)。
还可以得到另外一个结论,也挺有用 :ax + by = 1
有整数解当且仅当GCD(a,b)=1(即a与b互质)
推导求解
- 对(a,b)做辗转相除法直到(c,0)
很显然,不是吗 - 由于c + 0 = c,可以得到此时必有一解(x,y) = (1,0) (事实上y是任意的,只不过为了防止造数据的无脑…你懂的)
- 假设现在我们已知
bx0 + (a mod b)y0 = c
,如何求出ax + by = c的解?(为什么要这么写?参考GCD的过程) - 因为
a mod b
比较难操作,所以要先将上式改写为bx0 + (a – [a/b] * b)y0 = c
(形如a mod b
的式子,一般来说在推导过程中都要化为a – [a/b] * b
) - 在做GCD时递归即可
上代码
#include<cstdio>
using namespace std;
int a,b;
struct ANS{
int x,y;
}ans;
void gcd(int x,int y){
if(!y) {ans.x=1;ans.y=0;}
else{
gcd(y,x%y);
int t=ans.x;
ans.x=ans.y;
ans.y=t-((int)(x/y))*ans.y;
}
}
int main(){
scanf("%d%d",&a,&b);
gcd(a,b);
printf("%d %d\n",ans.x,ans.y);
return 0;
}
顺便给一个Python版的代码
class ANS:
x=0
y=0
def __init__(self):
self.x=0
self.y=0
def gcd(x,y):
global ans
if not y:
ans.x=1
ans.y=0
else:
gcd(y,x%y)
t=ans.x
ans.x=ans.y
ans.y=t-(int(x/y))*ans.y
ans=ANS()
a=0
b=0
a,b = [int(j) for j in raw_input().split()]
gcd(a,b)
print str(ans.x)+' '+str(ans.y)