最大公约数与最小公倍数

概念

  1. 公约数:有 k 个非零整数 a_{1},a_{2},...,a_{k},若 \exists \: d \in Z^+,使得a_{1},a_{2},...,a_{k}能被d整除,则称 d 为 a_{1},a_{2},...,a_{k} 的公约数。
  2. 最大公约数:公约数中的最大值称为最大公约数,记为:GCD(a_1,a_2,...,a_k)
  3. 公倍数:有 k 个非零整数 a_{1},a_{2},...,a_{k},若 \exists \: d \in Z^+,使得d能被a_{1},a_{2},...,a_{k}整除,则称 d 为 a_{1},a_{2},...,a_{k} 的公倍数。
  4. 最小公倍数:公倍数中的最小值称为最小公倍数,记为:LCM(a_1,a_2,...,a_k)

     最大公约数一定是存在的,其最小值为 1;当 其值为1 时,则数互质。

     公约数一定是最大公约数的约数,公倍数一定是最小公倍数的倍数。

 

最大公约数

1.辗转相除法/欧几里得算法

原理:GCD(x,y)=GCD(x,y-x),x>y,即:GCD(x,y)=GCD(y,x\:\, mod\:\, y),x>y


int gcd(int x, int y)
{
	if(x%y == 0)
		return y;
	else return (y, x%y);
}
int gcd(int a, int b)
{
	int r = a%b;
	while(r)
	{
		a = b;
		b = r;
		r = a%b;
	}
	return b;
}

2.二进制欧几里得算法

为提高效率,可将欧几里德算法进行优化,通过不断去除因子 2 来降低常数。

  • 当 x=y 时,GCD(x,y)=x=y
  • 当 x、y 均为偶数时,GCD(x,y)=2*GCD(x/2,y/2)
  • 当 x 为偶数,y 为奇数时,GCD(x,y)=GCD(x/2,y)
  • 当 x 为奇数,x 为偶数时,GCD(x,y)=GCD(x,y/2)
int GCD(int x, int y)
{
	if(x == 0)
		return y;
	if(y == 0)
		return x;
	for(int i=0; !(x&1); i++) //约去2
		x >>= 1;
	for(int j=0; !(y&1); j++) //约去2
		y >>= 1;
	if(i>j)
		i = j;
	while(true)
	{
		if(x < y) //若x<y,交换两数,保证大数在前
		{
			x ^= y;
			y ^= x;
			x ^= y;
		}
		if((x-=y) == 0)//辗转减
			return y<<i;
		while(!(x&1))//约去2
			x >>= 1;
	}
}

3.拓展欧几里得

在已知 x、y 时,求解一组 a、b,使得 a*x+b*y=GCD(x,y).

设 x>y

① 当 y=0 时,GCD(x,y)=x,此时 a=1,b=0

② 当 x>y>0 时

     设 a_1*x+b_1*y=GCD(x,y)a_2*y+b_2*(x \: \, mod\: \, y)=GCD(y,x \: \, mod\: \, y)

     由于 GCD(x,y)=GCD(y,x \: \, mod\: \, y)

     故 a_1*x+b_1*y=a_2*y+b_2*(x \: \, mod\: \, y)

     即 a_1*x+b_1*y=a_2*y+b_2*(x-[x/y]*y)=a_2*y+b_2*x- b_2*[x/y] *y

     因此 a_1*x+b_1*y=b_2*x+(a_2- [x/y] * b_2)*y

     易得  \left\{\begin{matrix}a1=b2 \\ b_1=a_2- [x/y] * b_2 \end{matrix}\right.

这样就得到了求解 a_1b_1 的方法:a_1b_1 的值基于 a_2b_2

由于 GCD 不断的递归求解,因此一定会在某时 b=0,结束递归,从而得出 a、b 的值。

x - [x/y]*y 即为 mod 运算,[x/y] 代表取小于 x/y 的最大整数。

(来源:https://blog.csdn.net/u011815404/article/details/81284222

int Extended_gcd(int x, int y, int &a, int &b)
{
	if(y == 0)
	{
		a = 1;
		b = 0;
		return x;
	}
	int gcd = Extended_gcd(b, a%b, y, x);
	b -= a*(x/y);
	return gcd;
}
int main()
{
	//形如 ax+by=GCD(x,y)
	int x, y, a, b;
	cin>>x>>y;
	int gcd = Extended_gcd(x,y,a,b);
	cout<<"GCD="<<gcd<<endl;
	cout<<"a="<<a<<",b="<<b<<endl;
	return 0;
}

4.内置

c++内置了最大公约数的计算函数:__gcd(),存在于头文件algorithm中。

以下源码来自stl_algo.h文件:

  /**
   *  This is a helper function for the rotate algorithm specialized on RAIs.
   *  It returns the greatest common divisor of two integer values.
  */
  template<typename _EuclideanRingElement>
    _EuclideanRingElement
    __gcd(_EuclideanRingElement __m, _EuclideanRingElement __n)
    {
      while (__n != 0)
	{
	  _EuclideanRingElement __t = __m % __n;
	  __m = __n;
	  __n = __t;
	}
      return __m;
    }

调用方法:

传送门

5.辗转相减法/更相减损术

以较大的数减较小的数,接着把所得的差与较小的数比较,并以大数减小数。继续这个操作,直到所得的减数和差相等为止。

对于0和负数要特判,否则死循环。

int gcd(int x, int y)
{
	if(x == y) return x;
	return x>y ? gcd(x-y, y) : gcd(y-x, x);
}
int gcd(int x, int y)
{
	while(x != y)
	{
		if(x>y)
			x -= y;
		else y -= x;
	}
	return x;
}

6.辗转相除法与辗转相减法的比较

更相减损术和辗转相除法的主要区别在于前者所使用的运算是“减”,后者是“除”。从算法思想上看,两者并没有本质上的区别,但是在计算过程中,如果遇到一个数很大,另一个数比较小的情况,可能要进行很多次减法才能达到一次除法的效果,从而使得算法的时间复杂度退化为O(N),其中N是原先的两个数中较大的一个。相比之下,辗转相除法的时间复杂度稳定于O(logN)。

最小公倍数

定理:a、b 两个数的最小公倍数乘以它们的最大公约数等于 a 和 b 本身的乘积。

即:LCM(a,b) * GCD(a,b) = a*b

在求 LCM 时,如果将 LCM 写成 a*b/GCD(a,b),a*b 可能会溢出,可以先除后乘,即:a/GCD(a,b)*b。

int gcd(int x, int y)
{
	return y ? gcd(y, x%y) : x;
}
int lcm(int x, int y)
{
	return x/gcd(x, y)*y;
}

性质

gcd(a , b) = gcd(b , a-b)

gcd(m*a , m*b) = m*gcd(a , b), m为一个自然数

gcd(a+m*b , b) = gcd(a , b)

m=gcd(a , b)  则gcd(a/m,b/m)=gcd(a,b)/m

gcd(a, lcm(b, c)) = lcm(gcd(a, b), gcd(a, c))

lcm(a, gcd(b, c)) = gcd(lcm(a, b), lcm(a, c))

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值