最大公约数大家都比较熟悉,找最大公约数的方法也有很多,有时候也会被当作面试题来进行考察,这次给大家介绍几种常见的算法:
设两个数为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)=2gcd(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)=22gcd(6,9)
6为偶数,9为奇数,利用a是偶数b是奇数的公式:gcd(6,9)=22gcd(3,9)
3为奇数,9为奇数,利用双奇数的公式,由于a小于b,要将两个数交换一下,然后利用公式gcd(9,3)=22gcd(6.3)
6为偶数,3为奇数,利用a为偶数b为奇数的公式,gcd(6,3)=22gcd(3,3)
此时a和b相等,均为3,则最大公约数为22*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);
}