求两数的最大公约数的四种方法【Java版】

Q:写一段代码,求出两个整数的最大公约数,尽量优化算法的性能

一、暴力枚举

public static int gcd(int a,int b){
    int big = a > b ? a : b;
    int small = a < b ? a : b;

    if(big % small == 0){
        return small;
    }

    for(int i = small / 2;i > 1; i++){
        if(small % i == 0 && big % i == 0){
            return i;
        }
    }
    return i;
}

这个方法可以实现功能,但是效率很低

假设传入的参数是 10000 和 10001,就需要循环 4999 次 

二、辗转相除法(欧几里德算法)

定理:两个正整数 a 和 b(a > b)的最大公约数等于 a 除以 b 的余数 c 和 b 之间的最大公约数

public static int gcd(int a,int b){
    int big = a > b ? a : b;
    int small = a < b ? a : b;

    if(big % small == 0){
        return small;
    }

    return gcd(big % small, small);
}

这种方法相对于暴力枚举性能上有所优化,但是存在一个问题: 

当两个整数较大时,做 a % b 取模运算的性能会比较差 

三、更相减损术 

定理:两个正整数  a 和 b(a > b)的最大公约数等于 a - b 的差值 c 和较小数 b 的最大公约数

public static int gcd(int a,int b){
    int big = a > b ? a : b;
    int small = a < b ? a : b;

    if(big % small == 0){
        return small;
    }

    return gcd(big - small, small);
}

这种方法避免了大整数取模可能出现的性能问题

但是,更相减损术依靠两数求差的方式来递归,运算的次数可能会远大于辗转相除法

更相减损术是不稳定的算法,当两数相差悬殊时,如计算 10000 和 1 的最大公约数需要递归 9999 次 

四、最终优化

结合辗转相除法和更相减损术的优势,在更相减损术的基础上使用位移运算 

总所周知,位移运算的性能非常好 

当 a 和 b 均为偶数时:

gcd(a,b) = 2 * gcd(a / 2, b / 2) = 2 * gcd(a >> 1, b >> 1) 

当 a 为偶数,b 为奇数时:

gcd(a,b) = gcd(a / 2, b) = gcd(a >> 1, b) 

当 a 为奇数,b 为偶数时:

gcd(a,b) = gcd(a, b / 2) = gcd(a, b >> 1) 

当 a 和 b 均为奇数时,先利用更相减损术运算一次: 

gcd(a,b) = gcd(b, a - b)

此时 a - b 必然是偶数,又可以继续进行位移运算 

这种方式在两数比较小时,可能看不出计算次数的优势,当两数越大时,计算次数的减少就会越明显 

public static int gcd(int a,int b){
    if(a == b) return a;

    if((a & 1) == 0 && (b & 1) == 0) return gcd(a >> 1, b >> 1) << 1;
    else if((a & 1) == 0 && (b & 1) != 0) return gcd(a >> 1, b);
    else if((a & 1) != 0 && (b & 1) == 0) return gcd(a, b >> 1);
    else{
        int big = a > b ? a : b;
        int small = a < b ? a : b;

        return gcd(big - small, small);
    }
}

参考资料:《漫画算法:小灰的算法之旅》

一  叶  知  秋,奥  妙  玄  心

  • 8
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

QX_Java_Learner

祝老板生意兴隆,财源广进!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值