最大公约数

  1. 问题:

写一个程序,求两个正整数的最大公约数。如果这两个整数都很大,有什么简单的算法吗?

 

  1. 思路:

        求最大公约数是一个很基本的问题,早在公元前300年左右,欧几里得就在他的著作《几何原本》中给出了高效的解法——辗转相除法。

         辗转相除法的原理很简单,设两数为a、b(a>b),b最大公约数(ab)的步骤如下:用ba,得a=bq......r1(0r1)。若r1=0,则(ab)=b;若r10,则再用r1b,得b=r1q......r2 (0r2.r2=0,则(ab)=r1,若r20,则继续用r2r1,……如此下去,直到能整除为止。其最后一个非零除数即为(ab的最大公约数。

         原理及其详细证明(来自百度百科http://baike.baidu.com/view/255668.htm

设两数为a、b(b<a),用gcd(a,b)表示ab的最大公约数,r=a mod b a除以b以后的余数,辗转相除法即是要证明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)dcb=nc=ycd,故ab最大公约数成为cd,而非c

从而可知gcd(b,r)=c,继而gcd(a,b)=gcd(b,r)。

证毕。

 

  1. 程序解法
  • 解法一

   最简单的递归解法,具体代码如下:

int gcd(int x, int y)
{
return (!y) ? x : gcd (y, x%y);
}

 

  • 解法二

   在解法一中,我们用到了取模运算,但对于大整数而言,取模运算(其中用到了除法)是非常昂贵的开销,有没有办法能够不用取模运算呢?

        采用类似前面辗转相除法的分析,如果一个数能够同时整除xy,则必能整除x-yy;反之亦然。即xy的公约数与x-yy的公约数是相同的,即gcd(x, y) = gcd(x - y, y),那么可转换成简单的多的大整数的减法。

   具体代码:

			long gcd(long x, long y)
			{
				if (x < y)
				{
					return gcd(y, x);
				}
				if (y == 0)
				{
					return x;
				}
				return gcd(x - y, y);
			}
  • 解法三

    解法二的问题在于迭代次数太多,在遇到(10000000 1)的情况就很悲催了。

           从分析公约数的特点入手:对于yx来说,如果y = k*y1 x = k * x1.那么有gcd(y, x) = k * gcd(y1, x1).

       另外,如果 x = p * x1, 假设p是素数,并且y%p != 0(y不能被p整除),那么gcd(x, y) = gcd(p * x1, y) = gcd(x1, y).

注意到这一点,我们取p = 2,并且将除以2的操作转换为右移一位,即得到以下代码:

 1             long gcd(long x, long y)
2 {
3 if (x < y)
4 {
5 return gcd(y, x);
6 }
7 if (y == 0)
8 {
9 return x;
10 }
11 if (IsEven(x)) //判断x是否为偶数
12 {
13 if (IsEven(y))
14 {
15 return (gcd(x >> 1, y >> 1) << 1);
16 }
17 else
18 {
19 return (gcd(x >> 1, y);
20 }
21 }
22 else
23 {
24 if (IsEven(y))
25 {
26 return gcd(x, y >> 1);
27 }
28 else
29 {
30 return gcd(x, x -y);
31 }
32 }
33 }



 

posted on 2012-03-07 12:26  aho  阅读(411)  评论(0编辑  收藏  举报

导航