辗转相除法+更相减损法求最大公约数

来源:互联网 发布:淘宝化妆品店铺推荐 编辑:程序博客网 时间:2024/05/17 05:14

怎么求两个数的最大公约数呢?
简单的想法就是直接暴力枚举,试出最大公约数

暴力枚举

#暴力枚举def GCD(numberA,numberB):    gcd=1    for i in range(2,max(numberA,numberB)+1):        if numberA%i == 0 and numberB%i == 0:            gcd=i    return gcd

这显然不是一个好的算法,那么应该怎么优化呢?

辗转相除法

公元前300年前就有了这类问题的解法:辗转相除法, 又名欧几里得算法(Euclidean algorithm),目的是求出两个正整数的最大公约数。它是已知最古老的算法。

这条算法基于一个定理:两个正整数a和b(a>b),它们的最大公约数等于a除以b的余数c和b之间的最大公约数。比如10和25,25除以10商2余5,那么10和25的最大公约数,等同于10和5的最大公约数。

#辗转相除法——欧几里得算法'''两个正整数a和b(a>b),它们的最大公约数等于a除以b的余数c和b之间的最大公约数。比如10和25,25除以10商2余5,那么10和25的最大公约数,等同于10和5的最大公约数。 缺点:做a%b运算的时候效率比较低'''      def GCD_Euclidean(numberA,numberB):    gcd=1    if numberA>numberB:        return getresult(numberA,numberB)    else :        return getresult(numberB,numberA)    return gcddef getresult(big,small):    if big%small is 0:        return small    else :        return getresult(small,big%small)

更相减损法

但是在取模运算的时候效率很低,有没有什么办法优化呢?

中国古代的《九章算术》中就有了相应的办法:更相减损术,也是一种求最大公约数的算法。

它的原理更加简单:两个正整数a和b(a>b),它们的最大公约数等于a-b的差值c和较小数b的最大公约数。比如10和25,25减去10的差是15,那么10和25的最大公约数,等同于10和15的最大公约数

#更相减损术, 出自于中国古代的《九章算术》,也是一种求最大公约数的算法。'''两个正整数a和b(a>b),它们的最大公约数等于a-b的差值c和较小数b的最大公约数。比如10和25,25减去10的差是15,那么10和25的最大公约数,等同于10和15的最大公约数。缺点:如果两数相差很远,如10000 和 1就需要迭代很多次'''def GCD_jiuzhang(numberA,numberB):    gcd=1    if numberA>numberB:        return get_jiuzhang(numberA,numberB)    else :        return get_jiuzhang(numberB,numberA)    return gcddef get_jiuzhang(numberA,numberB):    if numberA is numberB:        return numberA    if numberA>numberB:        return GCD_jiuzhang(numberB,numberA-numberB)    else:        return GCD_jiuzhang(numberB,numberB-numberA)

但是计算1000000和1的时候需要递归很多次,有没有什么办法优化呢?

此时应该将前面提到的,辗转相除法和更相减损术结合起来,在更相减损术的基础上用上移位运算

辗转相除法+更相减损法

众所周知,移位运算的性能非常快。对于给定的正整数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必然是偶数,又可以继续进行移位运算。

def GCD_Ecu_jiuzhang(numberA,numberB):    gcd=1    if numberA>numberB:        return get_Ecu_jiuzhang(numberA,numberB)    else :        return get_Ecu_jiuzhang(numberB,numberA)    return gcddef get_Ecu_jiuzhang(numberA,numberB):    if numberA is numberB:        return numberA    if numberA<numberB:        return get_Ecu_jiuzhang(numberB,numberA)#保证每次都是前一个数大于后一个数    else:        #都是偶数的情况        if numberA%2 == numberB%2 ==0:            return get_Ecu_jiuzhang(numberA>>1,numberB>>1)<<1        #A是偶数,B是奇数        elif numberA%2==0 and numberB%2!=0:            return get_Ecu_jiuzhang(numberA>>1,numberB)        #A是奇数,B是偶数        elif  numberB%2==0 and numberA%2!=0:            return get_Ecu_jiuzhang(numberA,numberB>>1)        #都是奇数        else:            return get_Ecu_jiuzhang(numberA-numberB,numberB)

参考
https://juejin.im/post/5a217bac51882531926e8656?utm_source=gold_browser_extension