求最大公约数之辗转相除法、更相减损术及两者结合算法

本文通过C语言实现并对比了多种求最大公约数的方法,包括原始暴力法、辗转相除法(欧几里得算法)、更相减损术及一种结合了前两种方法优势的最优算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

下面代码受漫画算法:辗转相除法是什么鬼?启发,原文讲解十分精彩,强烈建议查看原文,但是原文代码有几个小错误,本文代码为修改后的版本。讲解均在代码注释。


/**< 测试几种求最大公约数的算法 */

#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;
}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值