常用的四大算法求解最大公约数,分解质因数法、短除法、辗转相除法、更相减损法。
-
Python 官网:https://www.python.org/
-
Free:大咖免费“圣经”教程《 python 完全自学教程》,不仅仅是基础那么简单……
自学并不是什么神秘的东西,一个人一辈子自学的时间总是比在学校学习的时间长,没有老师的时候总是比有老师的时候多。
—— 华罗庚
- My CSDN主页、My HOT博、My Python 学习个人备忘录
- 好文力荐、 老齐教室
本文质量分:
CSDN质量分查询入口:http://www.csdn.net/qc
- ◆最大公约数
- 1、题目
- 1.1 题图示例代码
- 1.2 示例代码品读
- 1.3 两个整数x、y,不用if实现令x < y
- 1.4 位运算交换两个整数
- 2、最大公约数
- 2.1 概念
- 2.2 求解法
- 2.2.1 质因数分解法
- 2.2.2 短除法
- 2.2.3 辗转相除法
- 2.2.4 更相减损法
- 2.3 “辗转相除法” VS “更相减损法”
- 3、常用结论
- 4、参考资料
- 5、完整源码
◆最大公约数
1、题目
看到图中那段代码,就勾起了我探寻“最大公约数”的原始欲望……
1.1 题图示例代码
求两个整数的最大公约数和最小公倍数。敲打代码的时候随手+上了注释,让代码更易读。😉
x = int(input("x = ")) # 输入两个整数x、y。
y = int(input("y = "))
if x > y: # 比较两个整数大小,使 x > y 。
temp = y
y = x
x = temp
# 遍历两个整数较小整数x~1的整数列表(range(x, 0, -1),从x、x-1、x-2、...、1。最后一个参数-1是步长,表示从x开始,每次递减1——往前数1——。),用穷举法找到分别整除于两个整数的整数(第一个找到的即是最大公约数,因为是从到到小穷举的。),即是两个整数的最大公约数。
for factor in range(x, 0, -1):
if x%factor==0 and y%factor==0: # 找到最大公约数,打印输出。据最大公约数计算最小公倍数并打印输出
print("{} 和 {} 的最大公约数是:{}".format(x, y, factor))
print("{} 和 {} 的最小公倍数是:{}".format(x, y, x*y//factor))
break # 已完成任务,退出轮询循环(for循环)。
1.2 示例代码品读
图片中的代码,算法是比较优的。从较小的整数开始,来除两个整数,每次递减1,当找到第一符合条件(同时整除两个整数)的约数,即是最大公约数(是由大到小穷举的)。
不过代码还可以稍作优化。两个整数的输入,可以用一条语句完成接收键盘字符键入、转整;赋值较小数为x的交换操作,Python允许直接操作(a, b = b, a);结果输出可以用Python最新推崇的“插值字符串格式化”一个print()搞定。
tip = f"\n{' 求两个整数的最大公约数 ':=^39}\n\n{'输入两个整数(如24 84):':>12}"
x, y = map(int, input(tip).strip().split()) # 输入两个整数x、y。
if x > y: # 比较两个整数大小,使 x > y 。
x, y = y, x # Python可以不借助变量直接交换两个变量。底层的操作,Python可以搞定。
k = 0
for factor in range(x, 0, -1):
k += 1
if x%factor==0 and y%factor==0: # 找到最大公约数,打印输出。据最大公约数计算最小公倍数并打印输出
print(f"{'':~^50}\n\n{'':>4}{x}和{y}的最大公约数是({x}, {y})={factor},最小公倍数是[{x}, {y}]={x*y//factor}。\n") #
print(f"{'':~^50}\n{'':>15}程序执行了{k}次遍历。\n")
break # 已完成任务,退出轮询循环(for循环)。
1.3 两个整数x、y,不用if实现令x < y
max()、min()内置函数。将两个整数整合成list、set、tuple(列表、集合、元组任意一种Python数据类型),较小数赋值x,较大数赋值y。
x, y = 6, 3
#print(x,y)
tem = (x, y) # 将两个整数整合成list、set、tuple。
x, y = min(tem), max(tem) # 较小数赋值x,较大数赋值y。
#print(x,y)
1.4 位运算交换两个整数
可以有
x = x^y
y = x^y
x = x^y
#print(x, y)
或者
x ^= y
y ^= x
x ^= y
#print(x, y)
两种写法,根本实质就是同一个。
下面,开启“最大公约数”探寻之旅……
2、最大公约数
2.1 概念
最大公因数,也称最大公约数、最大公因子,指两个或多个整数共有约数中最大的一个。
a,b的最大公约数记为(a,b),同样的,a,b,c的最大公约数记为(a,b,c),多个整数的最大公约数也有同样的记号。求最大公约数有多种方法,常见的有质因数分解法、短除法、辗转相除法、更相减损法。
如果数a能被数b整除,a就叫做b的倍数,b就叫做a的约数。约数和倍数都表示一个整数与另一个整数的关系,不能单独存在。如只能说16是某数的倍数,2是某数的约数,而不能孤立地说16是倍数,2是约数。
"倍"与"倍数"是不同的两个概念,"倍"是指两个数相除的商,它可以是整数、小数或者分数。"倍数"只是在数的整除的范围内,相对于"约数"而言的一个数字的概念,表示的是能被某一个自然数整除的数。
几个整数中公有的约数,叫做这几个数的公约数;其中最大的一个,叫做这几个数的最大公约数。例如:12、16的公约数有1、2、4,其中最大的一个是4,4是12与16的最大公约数,一般记为(12,16)=4。12、15、18的最大公约数是3,记为(12,15,18)=3。
几个自然数公有的倍数,叫做这几个数的公倍数,其中最小的一个自然数,叫做这几个数的最小公倍数。例如:4的倍数有4、8、12、16,……,6的倍数有6、12、18、24,……,4和6的公倍数有12、24,……,其中最小的是12,一般记为[4,6]=12。12、15、18的最小公倍数是180。记为[12,15,18]=180。若干个互质数的最小公倍数为它们的乘积的绝对值。
2.2 求解法
2.2.1 质因数分解法
把每个数分别分解质因数,再把各数中的全部公有质因数提取出来连乘,所得的积就是这几个数的最大公约数。
例如:求24和60的最大公约数,先分解质因数,得24=2×2×2×3,60=2×2×3×5,24与60的全部公有的质因数是2、2、3,它们的积是2×2×3=12,所以,(24,60)=12。
把几个数先分别分解质因数,再把各数中的全部公有的质因数和独有的质因数提取出来连乘,所得的积就是这几个数的最小公倍数。
例如:求6和15的最小公倍数。先分解质因数,得6=2×3,15=3×5,6和15的全部公有的质因数是3,6独有质因数是2,15独有的质因数是5,2×3×5=30,30里面包含6的全部质因数2和3,还包含了15的全部质因数3和5,且30是6和15的公倍数中最小的一个,所以[6,15]=30。
我对算法稍作了些调整:只分解较小整数的质因数(用质数判定函数解析较小数的质因数集合),然后遍历质因数集合,找寻两数公所有因数并求积,积即是“最大公约数”。
def isprime(n):
''' 素数判定 '''
if n < 2: return
for i in range(2, n):
if n%i == 0: return
return n
def gcd(a, b):
''' “分解质因数”求最大公约数和最小公倍数 '''
com = 1 # 最大公约数初值。
if a > b: a, b = b, a # 比较两个整数大小,使 x > y 。Python可以不借助变量直接交换两个变量。底层的操作,Python可以搞定。
for i in (i for i in range(a+1) if isprime(i)): # 解析式解析较小数以内的所有质数。
while True:
if a%i == b%i == 0:
com *= i # 是公因数,求累积。
a, b = a/i, b/i
else: break
return com, int(com*a*b) # 返回最大公约数和最小公倍数。
2.2.2 短除法
短除法求最大公约数,先用这几个数的公约数连续去除,一直除到所有的商互质为止,然后把所有的除数连乘起来,所得的积就是这几个数的最大公约数。
短除法求最小公倍数,先用这几个数的公约数去除每个数,再用部分数的公约数去除,并把不能整除的数移下来,一直除到所有的商中每两个数都是互质的为止,然后把所有的除数和商连乘起来,所得的积就是这几个数的最小公倍数,例如,求12、15、18的最小公倍数。
短除法的本质就是质因数分解法,只是将质因数分解用短除符号来进行。
短除符号就是除号倒过来。短除就是在除法中写除数的地方写两个数共有的质因数,然后落下两个数被公有质因数整除的商,之后再除,以此类推,直到结果互质为止(两个数互质)。
而在用短除计算多个数时,对其中任意两个数存在的因数都要算出,其它没有这个因数的数则原样落下。直到剩下每两个都是互质关系。
求最大公因数便乘一边,求最小公倍数便乘一圈。
无论是短除法,还是分解质因数法,在质因数较大时,都会觉得困难。这时就需要用新的方法。
代码实现对算法稍作调整,从较小整数开始,逆序对两个整数试商,直到找出公约数。此法实乃题目图片中的代码所用“算法”。
def gcd(a, b):
''' “短除法”求最大公约数和最小公倍数 '''
if a > b:
a, b = b, a # 比较两个整数大小,使 x > y 。Python可以不借助变量直接交换两个变量。底层的操作,Python可以搞定。
for i in range(a, 0, -1):
if a%i == b%i == 0: # 从较小数开始倒逆穷举试炼,查找最大公约数。
return i, int(a*b/i)
#return 1, a*b # 此条语句多余,此就是互质数遍历到最后1,穷举到了最后。
2.2.3 辗转相除法
辗转相除法是求两个自然数的最大公约数的一种方法,也叫欧几里德算法。
例如,求(319,377):
∵319÷377=0(余319)
∴(319,377)=(377,319);
∵377÷319=1(余58)
∴(377,319)=(319,58);
∵319÷58=5(余29)
∴(319,58)=(58,29);
∵58÷29=2(余0)
∴(58,29)=29;
∴(319,377)=29。
可以写成右边的格式。
用辗转相除法求几个数的最大公约数,可以先求出其中任意两个数的最大公约数,再求这个最大公约数与第三个数的最大公约数,依次求下去,直到最后一个数为止。最后所得的那个最大公约数,就是所有这些数的最大公约数。
代码既可以用while 循环,也可以用递归函数求解。
while循环
def gcd(a, b):
''' “辗转相除法”求最大公约数和最小公倍数 '''
product = a*b # 定义两数之积存储变量(计算最小公倍数用),后面的运算会改写a、b的值。
while True:
c = a%b
b = a
a = c
if c == 0: # 余数为0,终止while,返回结果。
return b, int(product/b)
[外链图片转存失败,源站可能有防盗在这里插入!链机制,建描述]议将图片上https://传(imblog.csdig.cn/8441zUV7865a89794d5f8c21facbcc62.jpg4641)(https://imdg-blog.csdnimg.cn/8447865a89794d5f8c1cf826fa4bcc62.jpg)]
递归函数
def gcd(a, b):
''' “辗转相除法”求最大公约数和最小公倍数 '''
product = a*b # 定义两数之积存储变量(计算最小公倍数用),后面的运算会改写a、b的值。
def gcd0(a, b):
c = a%b
if c == 0:
return b # 求得解,终止递归。
else:
return gcd0(b, c) # 递归求解。
gcd = gcd0(a, b) # 调用递归求最大公约数。
return gcd, product//gcd # 返回最大公约数和最小公倍数。
2.2.4 更相减损法
也叫更相减损术,是出自《九章算术》的一种求最大公约数的算法,它原本是为约分而设计的,但它适用于任何需要求最大公约数的场合。
《九章算术》是中国古代的数学专著,其中的“更相减损术”可以用来求两个数的最大公约数,即“可半者半之,不可半者,副置分母、子之数,以少减多,更相减损,求其等也。以等数约之。”
翻译成现代语言如下:
第一步:任意给定两个正整数;判断它们是否都是偶数。若是,则用2约简;若不是则执行第二步。
第二步:以较大的数减较小的数,接着把所得的差与较小的数比较,并以大数减小数。继续这个操作,直到所得的减数和差相等为止
则第一步中约掉的若干个2与第二步中等数的乘积就是所求的最大公约数。
其中所说的“等数”,就是最大公约数。求“等数”的办法是“更相减损”法。所以更相减损法也叫等值算法。
例1.用更相减损术求98与63的最大公约数
解:由于63不是偶数,把98和63以大数减小数,并辗转相减:
98-63=35
63-35=28
35-28=7
28-7=21
21-7=14
14-7=7
所以,98和63的最大公约数等于7。
这个过程可以简单的写为:
(98,63)=(35,63)=(35,28)=(7,28)=(7,21)=(7,14)=(7,7)=7。
例2.用更相减损术求260和104的最大公约数。
解:由于260和104均为偶数,首先用2约简得到130和52,再用2约简得到65和26。
此时65是奇数而26不是奇数,故把65和26辗转相减:
65-26=39
39-26=13
26-13=13
所以,260与104的最大公约数等于13乘以第一步中约掉的两个2,即1322=52。
这个过程可以简单地写为:
(260,104)(/2/2)=>(65,26)=(39,26)=(13,26)=(13,13)=13.(22)=>52
def gcd(a, b):
''' “更相减损法”求最大公约数和最小公倍数 '''
product = a*b
twos = 1 # 两个整数可以除的2的积初值。
while True: # 一、去偶。
if a%2 == b%2 == 0:
a, b = a//2, b//2
twos *= 2
else:
break
def gcd0(a, b): # 二、循环较大数 - 较小数(减数、差)。
''' 递归函数 '''
if a > b: a, b = b, a
c = b - a # 每次减数、差,较大数 - 较小数
if a == c:
return a
else:
return gcd0(a, c)
com = gcd0(a, b)*twos # 计算最大公约数。
return com, int(product/com)
2.3 “辗转相除法” VS “更相减损法”
在求解最大公约数的几种方法中,辗转相除法最为出名。比较辗转相除法与更相减损法的区别:
<font color=#4F999F SI*
-
都是求最大公因数的方法,计算上辗转相除法以除法为主,更相减损术以减法为主,计算次数上辗转相除法计算次数相对较少,特别当两个数字大小区别较大时计算次数的区别较明显。
-
从结果体现形式来看,辗转相除法体现结果是以相除余数为0则得到,而更相减损术则以减数与差相等而得到。
3、常用结论
在解有关最大公约数、最小公倍数的问题时,常用到以下结论:
i. 如果两个自然数是互质数,那么它们的最大公约数是1,最小公倍数是这两个数的乘积。
例如8和9,它们是互质数,所以(8,9)=1,[8,9]=72。
ii. 如果两个自然数中,较大数是较小数的倍数,那么较小数就是这两个数的最大公约数,较大数就是这两个数的最小公倍数。
例如18与3,18÷3=6,所以(18,3)=3,[18,3]=18。
iii. 两个整数分别除以它们的最大公约数,所得的商是互质数。
例如8和14分别除以它们的最大公约数2,所得的商分别为4和7,那么4和7是互质数。
iv. 两个自然数的最大公约数与它们的最小公倍数的乘积等于这两个数的乘积。
例如12和16,(12,16)=4,[12,16]=48,有4×48=12×16,即(12,16)×[12,16]=12×16。
v. GCD(a,b) is the smallest positive linear combination of a and b. a与b的最大公约数是最小的a与b的正线性组合,即对于方程xa+yb=c来说,若x,a,y,b都为整数,那么c的最小正根为gcd(a,b)。
4、参考资料
- 最大公约数百科词条
https://baike.quark.cn/c/lemma/64969861344567#/index - 在线最大公约数计算工具·Json
http://www.jsons.cn/maxdivisor/
5、完整源码
(源码较长,点此跳过源码)
#!/sur/bin/nve python
# coding: utf-8
'''
The greatest common divisor(factor)
最大公约数(公因式)
filenane = 'gcd.py'
'''
# max()、min(),不用if语句,使x < y。
x, y = 6, 3
#print(x, y)
tem = (x, y) # 将两个整数整合成list、set、tuple。
x, y = min(tem), max(tem) # 较小数赋值x,较大数赋值y。
#print(x, y)
# 二、位运算交换两个整数
x = x^y
y = x^y
x = x^y
#print(x, y)
# or
x ^= y
y ^= x
x ^= y
#print(x, y)
def demo():
''' 题图中的示例代码 '''
x = int(input("x = ")) # 输入两个整数x、y。
y = int(input("y = "))
if x > y: # 比较两个整数大小,使 x > y 。
temp = y
y = x
x = temp
# 遍历两个整数较小整数x~1的整数列表(range(x, 0, -1),从x、x-1、x-2、...、1。最后一个参数-1是步长,表示从x开始,每次递减1——往前数1——。),用穷举法找到分别整除于两个整数的整数(第一个找到的即是最大公约数,因为是从到到小穷举的。),即是两个整数的最大公约数。
for factor in range(x, 0, -1):
if x%factor==0 and y%factor==0: # 找到最大公约数,打印输出。据最大公约数计算最小公倍数并打印输出
print("{} 和 {} 的最大公约数是:{}".format(x, y, factor))
print("{} 和 {} 的最小公倍数是:{}".format(x, y, x*y//factor))
break # 已完成任务,退出轮询循环(for循环)。
def demo():
''' “代码优化”题图中的示例代码:求最大公约数和最小公倍数。'''
tip = f"\n{' 求两个整数的最大公约数 ':=^39}\n\n{'输入两个整数(如24 84):':>12}"
x, y = map(int, input(tip).strip().split()) # 输入两个整数x、y。
if x > y: # 比较两个整数大小,使 x > y 。
x, y = y, x # Python可以不借助变量直接交换两个变量。底层的操作,Python可以搞定。
k = 0
# 遍历两个整数中小的整数x到1的整数列表(range(x, 0, -1),从x、x-1、x-2、...、1,最后一个参数 -1 是步长,表示从 x 开始,每次递减 1 ——往前数1——。),用穷举法找到分别整除于两个整数的整数(第一个找到的即是最大公约数,因为是从到到小穷举的。)。
for factor in range(x, 0, -1):
k += 1
if x%factor==0 and y%factor==0: # 找到最大公约数,打印输出。据最大公约数计算最小公倍数并打印输出
print(f"{'':~^50}\n\n{'':>4}{x}和{y}的最大公约数是({x}, {y})={factor},最小公倍数是[{x}, {y}]={x*y//factor}。\n") #
print(f"{'':~^50}\n{'':>15}程序执行了{k}次遍历。\n")
break # 已完成任务,退出轮询循环(for循环)。
def isprime(n):
''' 素数判定 '''
if n < 2: return
for i in range(2, n):
if n%i == 0: return
return n
def gcd(a, b):
''' “分解质因数”求最大公约数和最小公倍数 '''
com = 1 # 最大公约数初值。
if a > b: a, b = b, a # 比较两个整数大小,使 x > y 。Python可以不借助变量直接交换两个变量。底层的操作,Python可以搞定。
for i in (i for i in range(a+1) if isprime(i)): # 解析式解析较小数以内的所有质数。
while True:
if a%i == b%i == 0:
com *= i # 是公因数,求累积。
a, b = a/i, b/i
else: break
return com, int(com*a*b) # 返回最大公约数和最小公倍数。
def gcd(a, b):
''' “短除法”求最大公约数和最小公倍数 '''
if a > b: a, b = b, a # 比较两个整数大小,使 x > y 。Python可以不借助变量直接交换两个变量。底层的操作,Python可以搞定。
for i in range(a, 0, -1):
if a%i == b%i == 0: # 从较小数开始倒逆穷举试炼,查找最大公约数。
return i, int(a*b/i)
#return 1, a*b # 此条语句多余,此就是互质数遍历到最后1,穷举到了最后。
def gcd(a, b):
''' “辗转相除法”求最大公约数和最小公倍数 '''
product = a*b # 定义两数之积存储变量(计算最小公倍数用),后面的运算会改写a、b的值。
while True:
c = a%b
b = a
a = c
if c == 0: # 余数为0,终止while,返回结果。
return b, int(product/b)
def gcd(a, b):
''' “辗转相除法”求最大公约数和最小公倍数 '''
product = a*b # 定义两数之积存储变量(计算最小公倍数用),后面的运算会改写a、b的值。
def gcd0(a, b):
c = a%b
if c == 0:
return b # 求得解,终止递归。
else:
return gcd0(b, c) # 递归求解。
gcd = gcd0(a, b) # 调用递归求最大公约数。
return gcd, product//gcd # 返回最大公约数和最小公倍数。
def gcd(a, b):
''' “更相减损法”求最大公约数和最小公倍数 '''
product = a*b
twos = 1 # 两个整数可以除的2的积初值。
while True: # 一、去偶。
if a%2 == b%2 == 0:
a, b = a//2, b//2
twos *= 2
else:
break
def gcd0(a, b): # 二、循环较大数 - 较小数(减数、差)。
''' 递归函数 '''
if a > b: a, b = b, a
c = b - a # 每次减数、差,较大数 - 较小数
if a == c:
return a
else:
return gcd0(a, c)
com = gcd0(a, b)*twos # 计算最大公约数。
return com, int(product/com)
if __name__ == '__main__':
from time import time
start_sec = time()
#demo()
tip = f"\n{'输入两个整数(如24 84):':>12}"
a, b = map(int, input(tip).strip().split()) # 输入两个整数。
gcd = gcd(a, b)
print(f"{'':~^50}\n\n{'':>10}({a}, {b}) = {gcd[0]}、[{a}, {b}] = {gcd[1]}\n\n{'':~^50}")
print(f"{'':>14}程序运算用时{time()-start_sec:.5f}秒\n\n")
__上一篇:__ 茶杯(N个杯子排成一排,第X个杯子藏有球,交换任意两个杯子K次后,找出藏球杯子位置)
__下一篇:__
我的HOT博:
- Hot:Python之魔幻切片(1207阅读)
- 练习:数列求和a, aa, aaa, ..., aa...aa(n个a)(1206阅读)
- 个人信息提取(1501阅读)
- 十六进制字符串转Python代码(utf-8字符串转十六进制字符串)(1025阅读)
- 生成100个随机正整数(1654阅读)
- 给定字符串提取姓名(字符串、list、re“零宽断言”)(1244阅读)
- 我的 Python.color() (Python 色彩打印控制)(1575阅读)
- python清屏(2416阅读)
- 回车符、换行符和回车换行符(2475阅读)
- Linux 脚本文件第一行的特殊注释符(井号和感叹号组合)的含义(1481阅读)
- random.sample()将在python 3.9x后续版本中被弃用(1346阅读)
- pandas 数据类型之 Series(1507阅读)
- 聊天消息敏感词屏蔽系统(字符串替换 str.replace(str1, *) )(1559阅读)
- 练习:银行复利计算(用 for 循环解一道初中小题)(1494阅读)
- pandas 数据类型之 DataFrame(3503阅读)
- :班里有人和我同生日难吗?(蒙特卡洛随机模拟法)(2298阅读)
- Python 续行符(\)“拯救”你的超长语句(1105阅读)
- Python字符串居中显示(3663阅读)
- 练习:求偶数和、阈值分割和求差( list 对象的两个基础小题)(1715阅读)
- 用 pandas 解一道小题(2083阅读)
- 可迭代对象和四个函数(1128阅读)
- “快乐数”判断(1299阅读)
- 罗马数字转换器(构造元素取模)(2392阅读)
- Hot:罗马数字(转换器|罗生成器)(5256阅读)
- Hot:让QQ群昵称色变的代码(44638阅读)
- Hot:斐波那契数列(递归| for )(4169阅读)
- 柱状图中最大矩形(1698阅读)
- 排序数组元素的重复起止(1299阅读)
- 电话拨号键盘字母组合(1485阅读)
- 密码强度检测器(2227阅读)
- 求列表平衡点(1891阅读)
- Hot: 字符串统计(4362阅读)
- Hot:尼姆游戏(聪明版首发)(3583阅读)
- 尼姆游戏(优化版)(1258阅读)
推荐条件 点阅破千
回页首
精品文章:
- 好文力荐:齐伟书稿 《python 完全自学教程》 Free连载(已完稿并集结成书,还有PDF版本百度网盘永久分享,点击跳转免费🆓下载。)
- OPP三大特性:封装中的property
- 通过内置对象理解python'
- 正则表达式
- python中“*”的作用
- Python 完全自学手册
- 海象运算符
- Python中的 `!=`与`is not`不同
- 学习编程的正确方法
来源:老齐教室
回页首
◆ Python 入门指南【Python 3.6.3】
好文力荐:
-
全栈领域优质创作者——寒佬(还是国内某高校学生)博文“非技术文—关于英语和如何正确的提问”,“英语”和“会提问”是学习的两大利器。
-
【8大编程语言的适用领域】先别着急选语言学编程,先看它们能干嘛
-
靠谱程序员的好习惯
CSDN实用技巧博文:
- 8个好用到爆的Python实用技巧
- python忽略警告
- Python代码编写规范
- Python的docstring规范(说明文档的规范写法)