最大公约数问题

首先从定义说起,所谓公约数,就是能够同时被若干个整数整除的数。而这些数中最大的那个,就叫做最大公约数(greatest common divisor,简称gcd)。


讲完了定义,下面介绍几种求解最大公约数的算法。

第一种算法就是欧几里得(Euclid)提出的辗转相除法。

记x,y的最大公约数为f(x,y)。如果x,y同时可以整除t,那么y,x%y也可以整除t。理由很简单,令k=x/y(k为整数,因为x/y可能是小数),b=x%y。则x=ky+b。因为x可以整除t,所以(ky+b)也可以整除t。其中ky很明显可以整除t,则需要b也可以整除t。b既是x%y,得证。那么求f(x,y)就可以转换成为求f(y,x%y)。这也就是辗转相除的精髓:用上一次的除数作为新的被除数,用上一次的余数作为新的除数,循环求解。最后当除数为0时,被除数就是最大公约数。


下面举个例子,比如求24和16的gcd:

f(24,16)=f(16,8)=f(8,0)=8

下面上代码:

int gcd(int x,int y)
{
  return y?gcd(y,x%y):x;
}
这种算法有一点弊端,就是当数很大的时候(成百上千位),取模的开销会很大。由此我们提出了第二种算法。



第二种求gcd的方法f(x,y)=f(x-y,y)

如果x,y能同时整除t,则x=k1*t,y=k2*t,x-y=(k1-k2)*t(x>y),则(x-y,y)可以同时整除t。值得一提的是,如果x<y则先把x,y位置对调,之后再迭代。当除数为0时,停止迭代,被除数就是gcd。

用这种方法求24和16的gcd如下:

f(24,16)=f(8,16)=f(16,8)=f(8,8)=f(0,8)=f(8,0)=8

继续上代码:

int gcd2(int x,int y)
{
  if(x<y)return gcd2(y,x);
  else if(y==0)return x;
  else return gcd2(x-y,y);
}
该函数仍然是定义成为int类,如果需要求大数的gcd则需要自己定义大数类并重载运算符,本篇只提供求解思路,大数类的定义就不赘述了。

由于仅仅是用了减法,所以计算开销大幅度降低。但是该算法仍然存在一定局限,考虑一种极端情况,求f(1000000000001,1)。这样的例子需要迭代的次数实在太多,有没有更好的算法呢?请看三号算法。


第三种求gcd的算法:

在讲第三种算法之前,首先先明确2点:

1.若x=k*x1,y=k*y1,则f(x,y)=f(k*x1,k*y1)=k*f(x1,y1)很明显k是公约数但不一定是最大公约数。

2.若x=p*x1,其中p是素数且y%p!=0(也就是说p是x的约数而不是y的约数,那么很显然p不是x,y的公约数),则f(x,y)=f(p*x1,y)=f(x1,y)

计算机采用的是二进制,二进制移位运算对应着乘以2和除以2。我们不妨就从此下文章。

令p=2

如果x,y都是偶数的话,则f(x,y)=2*f(x/2,y/2)=[f(x>>1,y>>1)]<<1

如果x为偶,y为奇数, 则f(x,y)=f(2*x1,y)=f(x1,y)=f(x>>1,y)

如果x为奇,y为偶数, 则f(x,y)=f(x,2*y1)=f(x,y1)=f(x,y>>1)

如果x,y都是奇数的话,则f(x,y)=f(x-y,y)


用这种方法求24和16的gcd:

f(24,16)=2*f(12,8)=4*f(6,4)=8*f(3,2)=8*f(3,1)=8*f(2,1)=8*f(1,1)=8*f(0,1)=8*f(1,0)=8

话不多说,上代码:

int gcd3(int x,int y)
{
  if(x<y)return gcd3(y,x)
  else if(y==0)return x
  else if(isEven(x) && isEven(y))return (gcd3(x>>1,y>>1)<<1);
  else if(isEven(x) && (!isEven(y)))return gcd3(x>>1,y);
  else if((!isEven(x)) && isEven(y))return gcd3(x,y>>1);
  else return gcd3(x-y,y);
}
需要说明一下,isEven是判断是否为偶数 ,在此不予赘述。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值