今天终于弄懂了扩展欧几里德算法,有了自己的理解,觉得很神奇,就想着写一篇博客。
在介绍扩展欧几里德算法之前,我们先来回顾一下欧几里德算法。
欧几里德算法(辗转相除法):
辗转相除法求最大公约数,高中就学了,但当时知其然不知其所以然,直到大学才真正理解它的精髓。
理解辗转相除,关键在于理解 gcd(a,b)==gcd(b,a%b)
那么怎么去理解呢?下面是我的理解:
首先对于非负整数a,b,一定可以写成 a=k*b+r(r<b) 的形式
令 g=gcd(a,b) ,则有 g|a ,即 g|(k*b+r)
又 g|b ,所以 g|r
当然,只证得 g 同时整除 b 和 r 依然不能得出 g==gcd(b,r),还可能是gcd(b,r)>g 。
于是我们假设存在 g‘>g(故g'不可能是 a,b 的公约数)
使得 g'|b 且 g'|r
则有 g'|(k*b+r) 即 g'|a
→ g' 是 a,b 的公约数,矛盾。
故 gcd(a,b)==gcd(b,r)(其中 r==a%b) 得证。
下面贴出欧几里德算法的递归和非递归代码:
//递归欧几里德算法
int gcd(int a,int b)
{
return b==?a:gcd(b,a%b);
}
//非递归欧几里德算法
int gcd(int a,int b)
{
int t;
while(b){
t=b;
b=a%b;
a=t;
}
return a;
}
值得注意的是:
1、任意正整数和 0 的最大公约数为正整数本身。
2、即使参数 a<b ,经过一次递归或循环之后,a,b 会自动交换,故开头不需要加 if(a<b)swap(a,b); 语句。
扩展欧几里德算法:
扩展欧几里德算法其实就是为了求出 ax+by=gcd(a,b) 的一个整数解 (x0,y0) ,然后就能得出全部整数解为 (x0+k*b,y0-k*a) 。
递归实现
扩展欧几里德算法的递归实现是基于递归求gcd的过程,它会在gcd函数递归完返回的时候逐步解出一个整数解(x,y) 。
让我们来简单模拟一下 gcd(a,b) 递归返回的过程(数学归纳法得出递推公式)
①在gcd递归到最深处(倒是第一层)的时候,即此时 b0==0 ,可得出方程 a0x+b0y=gcd(a,b) (gcd(a,b)==gcd(a0,b0)==gcd(a1,b1)==…==a0)的一组整数解为x0=1,y0=0。
②假设返回到倒数第 k 层时方程 akx+bky=gcd(a,b) 的一组整数解为 xk , yk
又设返回到倒数第 k+1 层时方程 ak+1x+bk+1y=gcd(a,b) 的一组整数解为 xk+1 , yk+1
则有 akxk+bkyk=ak+1xk+1+bk+1yk+1 其中 ak==bk+1 , bk=ak+1%bk+1
不妨令 xk+1=yk yk+1=xk-ak+1/bk+1*yk
下面贴出递归代码:
//递归扩展欧几里得算法
int exgcd(int a,int b,int&x,int&y)
{
if(b==){
x=,y=;
return a;
}
int r=exgcd(b,a%b,y,x);
y-=a/b*x;
return r;
}
非递归实现
个人感觉扩展欧几里德算法的非递归方式理解起来更为简单。
我们就从一个具体的例子入手吧。
若 a=30 , b=47 , 则 gcd(a , b)=1,那么就是要求解一次不定方程 30x+47y=1 的整数解。
由已知,我们有
观察上述矩阵,第三列的元素变换过程恰好是辗转相除法求最大公约数的过程。
他们的每一步的变换形式(行变换)可用如下通式表示:
当第 2 行 3 列的元素为 0 时,第 1 行 3 列的元素即为 gcd(a , b)。
故最终能得出一组整数解 (x , y)=(11 , -7) 满足 30x+47y=1 。
于是我们可以在非递归欧几里德算法的基础上写出非递归的扩展欧几里德算法,代码如下:
//非递归扩展欧几里得算法
int exgcd(int a,int b,int&x,int&y)
{
int m=,n=,t;
x=,y=;
while(b){
t=m,m=x-a/b*m,x=t;
t=n,n=y-a/b*n,y=t;
t=b,b=a%b,a=t;
}
return a;
}
写到这里算是终于写完了。
这是我写的第一篇博客,本来旨在用最简洁的语言描述最核心的思想,但貌似……可能……大概……与预期有点不符...
我希望通过写博客记录一下自己的想法,即使日后忘记也能一看便知。当然,如果能帮助别人深入理解,那就再好不过了。
最后,如果各位发现文中的错误、有什么建议或者有一些自己的想法,都欢迎在评论区提出。