怎么求两个数的最大公约数呢?
简单的想法就是直接暴力枚举,试出最大公约数
暴力枚举
#暴力枚举
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 gcd
def 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 gcd
def 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 gcd
def 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