欧几里得算法又称作辗转相除法,作用是求解两个整数的最大公因数的方法
欧几里得算法怎么用?
假定我们在计算机中求a, b两个数的最大公因数,可以自定义一个函数 gcd (a, b) ,该函数的返回值 c 代表a, b的最大公因数,那么根据欧几里得算法,我们可以将返回值 c 用 gcd ( b, a % b)来表示(证明在下文),也就是说,a,b的最大公因数也是 a 与 a % b 的最大公因数,之后我们不断向下迭代,直到gcd( x, 0 )时,x 的值就是 a,b的最大公因数,即 c = x。
如何证明gcd(a, b)= gcd(b, a % b)呢?
证明比较繁杂,请读者一定要配合纸笔进行同步计算。
首先我们需要明确这样的一个式子:
a➗b = k ...... r( k是商,也可以看成是a/b向下取整,r是余数) 一定要记住这个式子,在接下来的证明中很重要!)
我们依旧设定 gcd(a, b)为a,b的最大公因数,这个最大公因数为 c 。
那么可知:c = gcd(a, b)
因此可推:
a = cx <1>
b = cy <2>
且 x 和 y 互质,为什么呢?如果x与y之间有因数,那吗c就不是最大公因数了,与最一开始的设定相违背。
那么我们再看a % b的值:
a % b = r = a - k * b(由红字公式推导而来)
将a = cx, b = cy,带入得:cx - kcy = r
将c提出,化简为:
c(x - ky) = r <3>
由此可见,c 也是 r 的因数,我们之前设定的 c 为 a 和 b 的最大公因数,a➗b的余数为 r ,现在可以得到结论:
余数r中,同样包含a和b的最大公因数c
那 a 和 b 的最大公因数 与 b 和 a % b 的最大公因数不就都是 c 吗?由此得证。
补充证明:其实在上面的证明中我们忽略了一个点,我们怎么证明<2><3>两个式子中的c是同一个c呢?我们只要证明<2> 中的 y 和<3> 中的 x - ky 是互质的就可以了。
接下来我们就进入到了第二阶段的证明:
如何证明 y ,与 x - ky 是互质的?
我们仍然可以设 gcd(x - ky , y)= d ,假定两者有最大公因数 d。
同第一阶段证明一样:
x - ky = md <4>
y = nd <5>
n,m一定互质,原因同 x,y 互质的原因相同。
对于式子<4>:移项:
x = md + ky
将<5>式中y带入:
x = knd + md = d ( kn + m ) <6> //m,n为正整数
现在已经得到了x,y的两个新的表现形式,将他们带入<1><2>式中,重新对a,b进行表示:
a = cd ( kn + m )
b = cnd
由此可知a,b会有一个公因数 cd。
也就是说 gcd(a, b)>= cd (当c不是最大公因数时,gcd(a,b)有可能大于cd),由于前面我们的设定为gcd(a,b)= c,替换之后为
c >= cd
由此可知 d = 1,即 y ,与 x - ky 是互质的,由此证毕。
这个算法放在代码中很好实现:
#include<stdio.h>
int gcd(int a,int b){
return b ? gcd(b, a % b) : a;
}
int main(){
int a, b;
scanf("%d%d", &a ,&b);
printf("%d\n",gcd(a,b));
return 0;
}
如何用欧几里得算法的特性解 ax + by = 1 这样类型的方程呢?
我们先来思考一个问题:
如果b = 0,a 已知的话,是否 x 的值就确定了呢?
答案是肯定的,x的值一定等于a分之1。
问题就来了,这和欧几里得算法,和 gcd 求最大公因数有什么样的联系呢?
回想一下gcd的截止条件:
当 b 为 0 时,返回 a 的值
是不是和上面的特殊情况有些相似?
如图,第一层代表原始方程,最后一层代表特殊解,也就是当q为0的情况。
现在如果想象我们对原始方程ax + by = 1中的a,b递归求gcd(a,b)的话,当递归到最后一层时,会不会同样也能得到一组特殊解x,y呢?当然x的值由a的值决定,而y可以是任意值。
这里设定x和y的两个系数p,q用来代表递归过程中gcd的两个参数(p,q)。
我们要知道,递归算法是依据栈完成的,也就是说,当最后一层gcd(p,0)得到最终答案x时,返回值就会开始回溯。不断返回,直到最一开始调用的gcd为止(栈空)。
在回溯的过程中,我们会对每一层的的p和q都了如指掌。因为最后一层的p是确定的。
既然如此,我们是否可以根据每一层的系数p,q,解出对应的x,y呢?这样当我们回溯到原始方程的系数时,对应的x,y也就解出了。
显然还不行,因为一个方程有两个未知量x,y,我们只是知道他们的系数还不足以解出答案。
怎么办?
当我们递归到最后一层时,一定会求出x的值,我们能否找到一个关系,使得本层的x与上一层的值x或y其中一个未知量产生对应关系式而求解出上层一个变量呢?这样我们就可以不断求解和回溯,直到求出原始方程的解了。
如何求出每层x的上层x或y的关系?
证明方法如上。
由此可见上一层的x,y是有对应关系的,关系在上图最后两行(= =)。
接下来上代码:
#include<stdio.h>
int ex_gcd(int a, int b, int *x, int *y){//注意,a,b必须互质!
if(b == 0){
*x = 1;
*y = 0;
return a;
}
int x1, y1, r;
r = ex_gcd(b, a % b, &x1, &y1);
*y = x1 - a / b * y1; //根据推导的每层x,y的关系而来
*x = y1; //同上
return r;
}
int main(){
int a, b, x, y;
while(~scanf("%d%d", &a, &b)){
int ret = ex_gcd(a, b, &x, &y);
printf("x : %d, y : %d, ret = %d\n", x, y, ret);//其实ret就是a,b的最大公约数
printf("%d * %d + %d * %d = %d\n", a, x, b, y, a * x + b * y);
}
return 0;
}
关于为什么a,b一定互质的原因:
其实我们的这个算法是根据扩展欧几里得算法而来的:
简单来说可以用下面的公式来表示:
gcd (a,b) = ax + by
而 1 就是一种特殊情况,当gcd (a, b) = 1时,也就是a,b互质时,才会有这种情况。
读者可以思考思考欧几里得扩展算法的作用,想想实际应用。
欢迎评论区提问!如有错误恳请指正!