一.辗转相除法
1.来源背景
辗转相除法, 又名欧几里德算法(Euclidean algorithm),是求两个正整数之最大公约数的算法。它是已知最古老的算法, 其可追溯至公元前300年前。
2.思路分析
用较小数除较大数,再用出现的余数(第一余数)去除除数,再用出现的余数(第二余数)去除第一余数,如此反复,直到最后余数是0为止。如果是求两个数的最大公约数,那么最后的除数就是这两个数的最大公约数。
(两个整数的最大公约数等于其中较小的数和两数的相除余数的最大公约数)
3.数学证明
设两数为a、b(a>b),用gcd(a,b)表示a,b的最大公约数,r=a (mod b) 为a除以b的余数,k为a除以b的商,即a÷b=kr。辗转相除法即是要证明gcd(a,b)=gcd(b,r)。
第一步:令c=gcd(a,b),则设a=mc,b=nc
第二步:根据前提可知r =a-kb=mc-knc=(m-kn)c
第三步:根据第二步结果可知c也是r的因数
第四步:可以断定m-kn与n互质(假设m-kn=xd,n=yd (d>1),则m=kn+xd=kyd+xd=(ky+x)d,则a=mc=(ky+x)cd,b=nc=ycd,则a与b的一个公约数cd>c,故c非a与b的最大公约数,与前面结论矛盾),因此c也是b与r的最大公约数。
从而可知gcd(b,r)=c,继而gcd(a,b)=gcd(b,r)。
证毕。
以上步骤的操作是建立在刚开始时r≠0的基础之上的。即m与n亦互质。
4.代码分析(c语言)
#include <stdio.h>
int main()
{
int num1,num2;
int r;
scanf("%d%d",&num1,&num2);
if(num2>num1) //保证num1>num2
{
r=num2; //如果num2>num1,交换变量,使num2<num1
num2=num1;
num1=r;
}
r=num1%num2; //令较大数取余较小数,得到余数
while(r) //相当于while(r!=0)
{
num1=num2; //此时num1的值等于除数的值
num2=r; //此时num2的值等于余数的值
r=num1%num2; //再用第一余数除以余数(第二余数),如此反复,直到最后余数是0为止
}
printf("%d",num2); //最后的除数就是这两个数的最大公约数。
return 0;
}
二.更相减损法
1.来源背景
更相减损术是出自《九章算术》的一种求最大公约数的算法,它原本是为约分而设计的,但它适用于任何需要求最大公约数的场合。
2.思路分析
“可半者半之,不可半者,副置分母、子之数,以少减多,更相减损,求其等也。以等数约之.”
第一步:任意给定两个正整数;判断它们是否都是偶数。若是,则用2约简;若不是则执行第二步。
第二步:以较大的数减较小的数,接着把所得的差与较小的数比较,并以大数减小数。继续这个操作,直到所得的减数和差相等为止。
则第一步中约掉的若干个2与第二步中等数的乘积就是所求的最大公约数。
其中所说的"等数",就是最大公约数。求"等数"的办法是"更相减损"法。
注意:通常认为,算法描述中的第一步"可半者半之"是指分子分母皆为偶数的时候,首先用2约简。因为更相减损术原先是专用来约分,所以并不用考虑最后计算结果时,要把第一步中约掉的若干个2再乘回去。加入这一步的原因可能是,分母、分子皆为偶数是在分数加减运算的结果中比较容易遇到的一种情况,用这种方法有可能减少数字的位数,简化计算。
当然,省略这个以2约简的步骤,也能得到正确的答案。
3.数学实例
例1、用更相减损术求98与63的最大公约数。
解:由于63不是偶数,把98和63以大数减小数,并辗转相减:
98-63=35
63-35=28
35-28=7
28-7=21
21-7=14
14-7=7
所以,98和63的最大公约数等于7。
例2、用更相减损术求260和104的最大公约数。
解:由于260和104均为偶数,首先用2约简得到130和52,再用2约简得到65和26。
此时65是奇数而26不是奇数,故把65和26辗转相减:
65-26=39
39-26=13
26-13=13
所以,260与104的最大公约数等于13乘以第一步中约掉的两个2,即1322=52。
4.代码分析
#include <stdio.h>
#include <math.h>
int main()
{
int num1,num2;
scanf("%d%d",&num1,&num2);
int i=0;//储存第一步中约掉的若干个2
int c;//储存最终返回的结果
while(num1%2==0&&num2%2==0)//如果ab均为偶数,则用2约简
{
num1=num1/2;
num2=num2/2;
i++;
} //用这种方法有可能减少数字的位数,简化计算,可以省略。
while(num1!=num2)//判断两数是否相等,也可以理解为直到所得的减数和差相等为止
{
if(num1>num2)
num1=num1-num2;//以较大的数减较小的数
else
num2=num2-num1;//以较大的数减较小的数
}
c=pow(2,i)*num1;//求第一步中约掉的若干个2与第二步中等数的乘积
printf("%d",c);
}
三.注意
1.比较
更相减损术和辗转相除法的主要区别在于前者所使用的运算是"减",后者是"除"。从算法思想上看,两者并没有本质上的区别,但是在计算过程中,如果遇到一个数很大,另一个数比较小的情况,可能要进行很多次减法才能达到一次除法的效果,从而使得算法的时间复杂度退化为O(N),其中N是原先的两个数中较大的一个。相比之下,辗转相除法的时间复杂度稳定于O(logN)。
2.最小公倍数的求法
可用两数之积除以两数最大公因数
//本文部分内容参考360百科