下面代码受漫画算法:辗转相除法是什么鬼?启发,原文讲解十分精彩,强烈建议查看原文,但是原文代码有几个小错误,本文代码为修改后的版本。讲解均在代码注释。
/**< 测试几种求最大公约数的算法 */
#include <stdio.h>
#include <stdlib.h>
int gcdOri(const int a, const int b);
int gcdEA(const int a, const int b);
int gcdMD(const int a, const int b);
int gcdBEST(const int a, const int b);
int main()
{
int a, b;
scanf("%d %d", &a, &b);
printf("Common algorithm: %d\n", gcdOri(a, b));
printf("Euclidean algorithm: %d\n", gcdEA(a, b));
printf("More derogation algorithm: %d\n", gcdMD(a, b));
printf("Best algorithm: %d\n", gcdBEST(a, b));
printf("Hello world!\n");
system("pause");
}
/** 最暴力的算法,从2遍历到a和b中较少的那个一半
* 效率最低,时间复杂度为O(n/2);
*/
int gcdOri(const int a, const int b)
{
int smallnum = a<b?a:b;
int bignum = a>b?a:b;
int i, rlt;
if(bignum%smallnum == 0)
rlt = smallnum;
else
{
for(i=2; i<=smallnum/2; i++)
{
if((bignum%i==0) && (smallnum%i==0))
{
rlt = i;
}
}
}
return rlt;
}
/** 辗转相除法, 又名欧几里得算法(Euclidean algorithm)
* 定理:两个正整数a和b(a>b),它们的最大公约数等于a除以b的余数c和b之间的最大公约数。
* 比如10和25,25除以10商2余5,那么10和25的最大公约数,等同于10和5的最大公约数。
* 但是当a、b较大是,a%b取模运算的性能比较低
*/
int gcdEA(const int a, const int b)
{
int rlt;
int smallnum = a<b?a:b;
int bignum = a>b?a:b;
if(bignum%smallnum == 0)
rlt = smallnum;
else
{
rlt = gcdEA(smallnum, bignum%smallnum);
}
return rlt;
}
/** 更相减损术, 出自于中国古代的《九章算术》
* 两个正整数a和b(a>b),它们的最大公约数等于a-b的差值c和较小数b的最大公约数。
* 比如10和25,25减去10的差是15,那么10和25的最大公约数,等同于10和15的最大公约数。
* 更相减算术依靠两数求差的方式来递归,运算的次数肯定远大于辗转相除法的取模方式
* 比如计算1000和1,就要递归999次
*/
int gcdMD(const int a, const int b)
{
int rlt;
int smallnum = a<b?a:b;
int bignum = a>b?a:b;
if(bignum%smallnum == 0) /**< 应该和bignum==smallnum一样 */
rlt = smallnum;
else
{
rlt = gcdMD(smallnum, (bignum-smallnum));
}
return rlt;
}
/** 最优方法:把辗转相除法和更相减损法的优势结合起来,在更相减算术的基础上使用移位运算
* 对于给定的正整数a和b,不难得到如下的结论。其中gcb(a,b)的意思是a,b的最大公约数函数:
* 当a和b均为偶数,gcb(a,b) = 2*gcb(a/2, b/2) = 2*gcb(a>>1, b>>1)
* 当a为偶数,b为奇数,gcb(a,b) = gcb(a/2, b) = gcb(a>>1, b)
* 当a为奇数,b为偶数,gcb(a,b) = gcb(a, b/2) = gcb(a, b>>1)
* 当a和b均为奇数,利用更相减损术运算一次,gcb(a,b) = gcb(b, a-b), 此时a-b必然是偶数,又可以继续进行移位运算。
*/
int gcdBEST(const int a, const int b)
{
int rlt = 0;
int smallnum = a<b?a:b;
int bignum = a>b?a:b;
if(bignum%smallnum == 0)
rlt = smallnum;
else
{
if(!(bignum&1) && !(smallnum&1)) /**< '&1'与1运算,就是2进制下末尾和1做‘与’运行,结果同1异0,所以可用来判断奇偶,返回1为奇,0为偶 */
{
rlt = gcdBEST(bignum>>1, smallnum>>1) << 1;
/**< 右移运算 >>1 相当于偶数/2,相反地,左移运算,相当于*2 */
}
else if(!(bignum&1) && smallnum&1)
{
rlt = gcdBEST(bignum>>1, smallnum);
}
else if(bignum&1 && !(smallnum&1))
{
rlt = gcdBEST(bignum, smallnum>>1);
}
else if(bignum&1 && smallnum&1)
{
rlt = gcdBEST(smallnum, bignum-smallnum);
}
}
return rlt;
}