最大公因数和最小公倍数算法


这里只介绍最大公因数算法,因为最小公倍数其实就是两数相乘再除以最大公因数

我们经常会遇到有关数论的题目,求解最大公因数便是常见的题目之一,以下为几种常见的方法,他们的主要结构均为递归

(1)辗转相除法

(个人比较喜欢这个算法,比较简洁)

这便是著名的欧几里得算法
Euclid 规则:如果 x 和 y 是正整数,且有 x>=y,那么 gcd(x,y)=gcd(x mod y,y)。

int gcd(int a,int b)
{
	if(a<b)
	{
		swap(a,b);
	}
	if(b==0)
	{
		return a;
	}
	return gcd(b,a%b); 
}

例如:求 25 和 11 两个数的最大公约数,即求 gcd(25,11)
a = x * b + y --> b = x’ * (a%b) + y’ 当 y‘==0 时,a%b 即为它俩的最大公约数
25 = 2 * 11 + 3
11 = 3 * 3 + 2
3 = 1 * 2 + 1
2 = 2 * 1 + 0

(2)更相减损法
百度百科中的介绍如下:
更相减损法, 又称 "等值算法"
“关于约分问题, 实质是如何求分子, 分母最大公约数的问题.《九章算术》中介绍了这个方法, 叫做” 更相减损术”, 即 “可半者半之,不可半者,副置分母、子之数,以少减多,更相减损,求其等也。以等数约之。”
  翻译成现代语言如下:
  第一步:任意给定两个正整数;判断它们是否都是偶数。若是,则用 2 约简;若不是则执行第二步。
  第二步:以较大的数减较小的数,接着把所得的差与较小的数比较,并以大数减小数。继续这个操作,直到所得的减数和差相等为止。
  则第一步中约掉的若干个 2 与第二步中等数的乘积就是所求的最大公约数。
  
非递归实现

int gcd2(int a,int b)
{
	int ans=1;
	while(a%2!=0&&b%2!=0)
	{
		a = a/2;
		b = b/2;
		ans=ans*2;
	}
	while(a != b)
	{
		if(a>b)
		{
			a = a-b;
		}else
		{
			b = b-a;
		}
	}
	return ans*a;
}

递归实现

int gcd2_2(int a,int b)
{
	if(a<b)
	{
		swap(a,b);
	}
	if(a==b)
	{
		return a;
	}
	int temp=a-b;
	if(temp<b)
	{
		return gcd2_2(b,temp); 
	}
	else
	{
		return gcd2_2(temp,b);
	}
	
}

递归实现实现需在 a,b 为奇数时实现
当 a,b 为任意正整数时,可用如下方式结合然后加以实现:

int gcd2(int a,int b)
{
	int ans=1;
	while(a%2!=0&&b%2!=0)
	{
		a = a/2;
		b = b/2;
		ans=ans*2;
	}
	a=gcd2_2(a,b);
	return ans*a;
}

例如:求 91 和 49 的最大公约数 gcd2(91,49)
91 - 49 = 42
49 - 42 = 7
42 - 7 = 35
35 - 7 = 28
28 - 7 = 14
14 - 7 = 7

(3)分治法
算法思想:
当 a,b 都是偶数时,gcd(a,b)=2*gcd(a/2,b/2);
当 a 是奇数,b 是偶数时,gcd(a,b)=gcd(a,b/2);
当 a 是偶数,b 是奇数时,gcd(a,b)=gcd(a/2,b);
当 a,b 都是奇数时,gcd(a,b)=gcd((a-b)/2,b).

代码实现如下:

int gcd3(int a,int b)
{
	if(a<b)
	{
		swap(a,b);
	}
	
	if(b==0)
	{
		return a;
	}
	if(a%2==0&&b%2==0)
	{
		return 2*gcd3(a/2,b/2);
	}
	else if(a%2!=0&&b%2==0)
	{
		return gcd3(a,b/2);
	}
	else if(a%2==0&&b%2!=0)
	{
		return gcd3(a/2,b);
	}
	else if(a%2!=0&&b%2!=0)
	{
		return gcd3((a-b)/2,b);
	}
}