通过本文希望能给程序设计的初学者一些启发。
本文重点讲述欧几里得算法,引出算法的三大前提,大概阐明算法的一些特点。
欧几里得算法(或辗转相除法)用于计算两个正整数的最大公约数,基本算法如下:
E:设两个正整数m,n,且已知m>n
E1:令r=m%n('%'代表取余)
E2:若r=0(即n整除m),结束运算,n即为结果
E3:否则令m=n,n=r,并返回步骤E1
欧几里得算法运用了这样一个等价式(设gcd(m,n)代表m和n的最大公约数,mod()代表取余运算或模运算)gcd(m,n)=gcd(n,mod(m,n))。也就是m,n的最大公约数等于他们相除余数(r)和n的最大公约数。
下面来证明一下这个等式:
1.因为任意两个正整数都有最大公因数,设为d
2.将m,n分别用最大公因数d来表示为m=k1d,n=k2d(k1,k2是两个常数)
3.设k3=m/n('/'代表相除取整),有r=m-k3n,将m,n代换得r=k1d-k3k2d,所以r=(k1-k3k2)d
由此可得r是最大公因数d的倍数,得证gcd(m,n)=gcd(n,mod(m,n)),所以以此类推,可以将m,n中较大的数用较小的余数r替换,实现了降维,所以有了E3中的步骤,而欧几里得算法的优点就在于此。
相比之下利用穷举法来计算最大公约数,其比较次数与计算量都大出很多。
C语言实现欧几里得算法代码如下:
//功能:计算两个正整数的最大公约数
//参数:m,n
//返回:m,n的最大公约数,类型为int整形
#include
#include
int gcd(int m,int n)
{
int t,r;
if (m
t=m;
m=n;
n=t;
}
while((m%n)!=0)//辗转相除
{
r=m%n;
m=n;
n=r;
}
return n;
}
int main()//主函数调用函数gcd()
{
int m,n;
scanf("%d%d",&m,&n);
printf("%d",gcd(m,n));
return 0;
}
采用穷举实现最大公约数的算法的C语言实现如下:
//功能:计算两个正整数的最大公约数
//参数:m,n
//返回:m,n的最大公约数,类型为int整形
#include
#include
int gcd(int m,int n)
{
int i=n;
for (i=n;i>1;i--)//逐个递减计算,递减计算不需要将全部数进行计算,当计算停止时就是最大值,若递增计算需要全部计算
{
if (m%i==0&&n%i==0)
{
break;
}
}
return i;
}
int main()//主函数调用函数gcd()
{
int m,n;
scanf("%d%d",&m,&n);
printf("%d",gcd(m,n));
return 0;
}
欧几里得算法是学习计算机最初接触到的最基础的几个算法之一,对初学者而言算法被看做是解决一类事件的通用方法,只要是满足事件(或者叫做满足这种关系)的参数按照算法步骤就能得到准确结果。其实这样理解可以说大体意思没有问题,但是缺少细节约束。
算法有三大约束条件:
一:有穷性 二:确切性 三:可行性
①有穷性
计算机顾名思义是用于计算的,如果无法计算出最终结果那就没有意义。当一个程序的步骤有无穷多,我们无法得到一个结果,这对于我们使用计算机的人来说就没有意义,例如下列代码:
for (i=0; ;i++)
{
}
上面代码可以得到一个无穷大的数,显而易见这是没有意义的。这样的代码段称为死循环,算法设计中一定避免死循环。我们回看欧几里得算法,他的有穷性是肯定的,因为任意两个正整数一定有公因数。
②确切性
这一部分初学者掌握的相对较好,在最初的程序设计中进行练习的大部分是数学问题,数学问题本身就是逻辑严密,确切性很好的问题。在日常生活中有些事情有着和算法一样的明确的步骤,但是会有一些表示程度的词汇(倒一些水,走得慢一点),一些,一点,这样的表示程度的词语使得最终的结果不会是唯一的,而算法中唯一的输入必然对应唯一的输出。算法步骤一定是逻辑严密,没有歧义的准确表达。这和计算机语言使用符号化的表示方式是一个道理。
欧几里得算法中,三个步骤是确切的,准确的表述
③可行性
计算机世界给我们带来的一直是快节奏,计算机的优点就是计算快速,所以我们也希望我们的算法步骤能在较快时间内运行结束。一个算法有穷,但是计算时间过长也是不被允许的。如果一个算法需要计算十年或者更长,那我们利用计算机的意义何在。
一个优秀的算法,不单是能够计算,能得到结果,他的运行效率也是相当重要的。