Lucky_Glass的程序笔记
第四期-扩展欧几里得算法
欧几里得算法是一种高效的求 gcd(a,b) 的算法,可以通过递推或递归计算。经过验算,我们发现——当a < b时,gcd(a,b)==gcd(a,b-a),否则等于 gcd(b,b-ka) (k为b整除a的值)。但是这样对栈空间的浪费极大,我们可以发现 b-ka 就是b取模a,即 b%a 。现在给出了一个问题——给定 a、b ,求出任意一组整数 x,y ,使得 xa+by==gcd(a,b) 。而求x、y的算法就是扩展欧几里得算法。
扩展欧几里得算法的推导
根据题意,我们可以令gcd(a,b)=f;则有xa+yb=f,且其中都只包含整数。
先从特殊的情况开始考虑——当递归到b为0时,则得到 xa+0*y=a ,那么y可以取任意值(这也是会有多组解的原因),但是x只能取1。
接下来考虑一般的情况。因为 gcd(a,b)=gcd(b,a%b) ,则令 bx’ + (a%b)y’ = gcd(b,a%b) 。那么 bx’ + ( a % b ) y’ = ax + by 。推导到这里,式中出现了一个 mod(取模),那么我们需要把mod拆开。这里用到了一个定义 “[a]”,意思是对a向0取整。那么 C++中的 a%b 用代数式就可以表示为:a - [ a / b ] * b,那么原式改为:
bx′+(a−[a/b]∗b)y′=ax+by
又可改为:
bx′+ay′−[a/b]∗b∗y′=ax+by
最后变为:
ay′+b∗(x′−[a/b]∗y′)=ax+by
根据对应项系数相等,则得——对于每一步的x,y都有: x=y′ 且 y=x′−[a/b]∗y′。扩展欧几里得算法的C++实现
接下来就是将算法代码化。我们可以对我们之前的gcd(a,b)函数进行一些修改,原本的gcd函数为:
int gcd(int a,int b)
{
if(b==0) return a;
return gcd(b,a % b);
}
我们需要将每一步的x、y都代入函数中。则可以应用 “传址” 的方法,在参数表中加入 x、y和sum(该次递归中的 ax+by 的值),即:
int gcd(int &x,int &y,int &sum,int a,int b);
当 b==0 时,则分别将 x、y 赋值为 1 和 0(y也可以赋值为其他数,但0较为方便),sum 赋值为 a 。 b != 0 时,先定义 sx、sy (即 x’ 和 y’),后执行gcd()函数。最后返回时,x 则赋值为 sy ,y则赋值为 sx - a / b * sy。如果不需要求出 gcd(a,b) 的话,函数是可以不返回值的。
3. 样例程序
void gcd(int& x,int& y,int sum,int a,int b)
{
if(b==0)
{
x=1;y=0;sum=a;
return;
}
int sx,sy;
gcd(sx,sy,sum,b,a%b);
x=sy;
y=sx-a/b*sy;
}
The End
Thanks for reading!
-Lucky_Glass