最近看了一本书《程序员》里面说的一个面试题:
求两个数的最大公约数:
SoEasy的题目看过C 的人都知道怎么写这个程序
1.传统方法:穷举
#include
int main()
{
int m=1970,n=1066,p=0;
p=mfor(;p>=1;p--)
{
Count++;
if(m%p==0&&n%p==0)
break;
}
printf("最大公约数是:%d /n",p);
printf("循环了:%d /n",Count);
return 0;
}
最大公约数是:2
循环了:1065次
2.辗转相除
#include
int main()
{
int m=1970,n=1066,p=0;
while(m%n != 0)
{
p = m%n;
m = n;
n = p;
Count++;
}
printf("最大公约数是%d/n",n);
printf("循环了%d/n",Count);
return 0;
}
最大公约数是:2
循环了:10
对于求1970和1066的最大公约数辗转相除法明显优秀很多。
我们可以将辗转相除法做一个变形。p=m%n 然后将p赋给n实际上就是一个交换 可以写成一个递归的形式如下:
int gcd(int m,int n)
{
if(n==0)//递归出口
return m;
else
return gcd(n,m%n);
}
这个函数就是传说中的欧几里德算法的描述了。
3.欧几里德算法
#include
int gcd(int m,int n);
int main(int argc, char* argv[])
{
int m=1970,n=1066;
printf("最大公约数是:%d/n",gcd(m,n));
return 0;
}
int gcd(int m,int n)
{
if(n==0)
{
return m;
}
else
{
return gcd(n,m%n);
}
}
最大公约数是:2
递归了:10
这个算法基本原理就是辗转相除,效率很高。
在面试的人中大部分都是采用第一种传统的方法。显然面试官想要的是第二种人了。其实从实现的过程来看后者要显得简单一些,递归很好理解。可能是我们都习惯于停用一种算法就解决问题。
4.扩展欧几里德算法
这个算法的并不是为了求最大公约数而设计的,但是它同样可以实现求最大公约数,原理就是Euclid。
扩展欧几里德算主要是解决乘法逆元的问题:A*BModC=1可以将B描述为:A模C的乘法逆元。好了看看算法吧:
int ExGcd(int a, int b, int &x, int &y);
int main(int argc, char* argv[])
{
int m=550,n=1769;
int x=0,y=0;
int k;
k=ExGcd(m,n,x,y);
printf("550 和 1769的最大公约数是:%d/n550模1769的乘法逆元是:%d/n1769模550的乘法逆元是:%d/n",k,x,y);
return 0;
}
/***************************************
函数:ExGcd
功能:求两个数的最大公约数和模P的乘法逆元。
输入:a,b 输入参数,求这两个数的最大公约数
和a模b的逆元 或 b模a的逆元。
输出:x,y 分别表示a模b的逆元和b模a的逆元。
返回:r 表示a b 的最大公约数。
*************************************/
int ExGcd(int a, int b, int &x, int &y)
{
if(b == 0)
{
x = 1;
y = 0;
return a;
}
int r = ExGcd(b, a % b, x, y);
int t = x;
x = y;
y = t - a / b * y;
return r;
}
程序中有详细的解释。
总结:积累高效算法很重要!