找最大公约数

最大公约数大家都比较熟悉,找最大公约数的方法也有很多,有时候也会被当作面试题来进行考察,这次给大家介绍几种常见的算法:

设两个数为a和b

1.辗转相除法

大数放在a中,小数放在b中,t为a模上b的余数
(1)首先对输入的a和b进行大小判断,确保将大数放在a中,小数放在b中
(2)求a模b,其值放在t中
(3)如果t为0,则b为最大公约数,也就是说a能够把b整除,因此b为最大公约数
(4)如果t不等于0,则把b的值给a,t的值给b,继续第二步的操作,直到t为0。
注意:这个算法的关键是a中必须放大数,b中放小数,不然a/b结果都是0,就无法进行判断。

int gcd1(int a, int b)
{
	if (a < b)
	{
		int m = a;
		a = b;
		b = m;
	}
	int t = 0;
	while (a % b != 0)
	{
		t = a%b;
		a = b;
		b = t;
	}
	return b;
}

2.穷举法

最大公约数一定是小于a和b中最小的那个数,因此最大公约数一定是在[1,a]之间的,可以用一一列举的方法来找到最大公约数。
注意:这个方法的缺点就是效率太低,如果a和b的数值都很大,就会十分浪费时间和效率。

int gcd2(int a, int b)
{
	int min = a;
	if (a > b)
		min = b;
	for(int i = min; i > 0; i--)
	{
		if (0 == a%i && 0 == b%i)
			return i;
	}
	return 1;
}

3.更相减损法

更相减损法是比较古老的一种算法,按照步骤解释如下:
第一步:首先判断a和b是不是都是偶数,如果都是偶数的话,就将a和b用2化简,直到两个数军不是偶数。
第二步:此时a和b为一奇一偶,将大数放在a中,小数放在b中,用a-b,也就是大数减去小数,将所得到的差值与较小的数比较,然后将大数赋值给a,小数赋值给b,再次用大数减小数,以此类推,直到得到的差值与较小的值相等。
第三步:计算最大公约数,如果用2化简得次数为0的话,最后的差值就是最大公约数;如果化简2的次数不是0的话,最大公约数就是化简的若干个2相乘(也就是2的n次方,n为化简2得次数)再乘以最后的差值。
注意:这个方法中,在两个数都不是偶数之后,要确保后面得相减是大数减小数,因此需要判断一下,并且最后的计算是有两种不同的情况,要进行分别计算。

#include <math.h>
int gcd3(int a, int b)
{
	int count = 0;
	while (0 == a % 2 && 0 == b % 2)
	{
		a /= 2;
		b /= 2;
		count++;
	}
	if (a < b)
	{
		int t = a;
		a = b;
		b = t;
	}

	while (a - b != b)
	{
		int t = a - b;
		a = b > t ? b : t;
		b = b < t ? b : t;
	}
	
	if (0 == count)
		return b;
	else
		return (int) pow(2,count) * b;
}

4.Stein算法
Stein算法是在辗转相除法的基础上进行改进的。
根据最大公约数的特性:gcd(kx,ky)=kgcd(x,y)
对于a>b来说,分为四种情况:
(1)a和b均为偶数:gcd(a,b)=2
gcd(a/2,b/2)
(2)a和b均为奇数:gcd(a,b)=gcd((a+b)/2,(a-b)/2)
(3)a为奇数,b为偶数:gcd(a,b)=gcd(a,b/2)
(4)a为偶数,b为奇数:gcd(a,b)=gcd(a/2,b)
这种算法看着复杂,但是效率是非常高的,举一个例子:a=24,b=36,求两个数的最大公约数。
首先两个数都是偶数,利用双偶数的公式gcd(24,36)=2gcd(12,18)
然后12和18均为偶数,再用双偶数的公式:gcd(12,18)=2
2gcd(6,9)
6为偶数,9为奇数,利用a是偶数b是奇数的公式:gcd(6,9)=2
2gcd(3,9)
3为奇数,9为奇数,利用双奇数的公式,由于a小于b,要将两个数交换一下,然后利用公式gcd(9,3)=2
2gcd(6.3)
6为偶数,3为奇数,利用a为偶数b为奇数的公式,gcd(6,3)=2
2gcd(3,3)
此时a和b相等,均为3,则最大公约数为2
2*3=12
这里有几点需要注意:
第一:在两个数都为奇数的时候,要用a-b,我们假设的都是大数减小数,因此需要进行判断a和b大小的步骤。
第二:两个数为奇数时,有(a+b)/2这一步,需要考虑溢出的问题,有一种比较巧妙的方法就可以防止溢出。先用(a-b)/2,然后用a-(a-b)/2就可以得到(a+b)/2
在这里插入图片描述

int gcd4(int a, int b)
{
	int count = 0;
	if (a < b)
	{
		int t = a;
		a = b;
		b = t;
	}
	while (a != b) //a和b不相等进入循环,如果相等,a和b任意一个都是最大公约数
	{
		//如果是偶数,最后一个二进制位为0;奇数则为1
		if (a & 1)//判断a是否为奇数
		{
			if (b & 1)//判断b是否为奇数
			{
				b = (a - b) >> 1;//相当于(a-b)/2
				a = a - b;//上面b=(a-b)/2之后,再用a-b就能够实现(a+b)/2,这样做是因为防止溢出
			}
			else
			{
				//a为奇数,b为偶数
				b >>= 1;//b/2
			}
		}
		else
		{
			//a是偶数
			if (b & 1)
			{
				//a是偶数,b是奇数
				a >>= 1;
				//a为偶数,b为奇数,之后要对大小进行判断
				if (a < b)
				{
					int t = a;
					a = b;
					b = t;
				}
			}
			else
			{
				//a和b均为偶数
				a >>= 1;
				b >>= 1;
				count++;
			}
		}

	}
	return (a << count);

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值