100个python算法超详细讲解:最大公约数

1.问题描述
求任意两个正整数的最大公约数(Greatest Common Divisor,GCD)。
2.问题分析
如果有一个自然数a能被自然数b整除,则称a为b的倍数,b为a的约数。几个自
然数公有的约数,叫作这几个自然数的公约数。公约数中最大的一个公约数,称为
这几个自然数的最大公约数。
根据约数的定义可知,某个数的所有约数必不大于这个数本身,几个自然数的
最大公约数必不大于其中任何一个数。要求任意两个正整数的最大公约数即求出一
个不大于这两个数中的任何一个,但又能同时整除两个整数的最大自然数。
3.算法设计
思路有两种,第一种,采用穷举法按从小到大(初值为1,最大值为两个整数
当中较小的数)的顺序将所有满足条件的公约数列出,输出其中最大的一个;第二
种,按照从大(两整数中较小的数)到小(最小的整数1)的顺序求出第一个能同
时整除两个整数的自然数,即为所求。下面对第二种思路进行详细说明。
无论按照从小到大还是从大到小的顺序寻找最大公约数,最关键的是找出两数
中较小的数。对于输入的两个正整数m和n,相同的数据可能因为输入顺序不同导致
变量m、n中存储数据的大小不同,如m=8、n=4与m=4、n=8,但无论变量中值的大
小顺序怎么样最后的结果应该是相同的。为了避免相同数据因输入顺序不同而出现
不同的结果,也为了使程序具有一般性,对于每次输入的值先进行大小排序,规定
变量m中存储大数、变量n中存储小数。
两个变量所存储内容互换需要借助一个中间变量来完成,若采用将第二个变量
的值赋给第一个变量,然后再将第一个变量的值赋给第二个变量的方法是错误的,
因为经过第一次赋值(即第二个变量的值赋给第一个变量)后,第一个变量中存储
的内容被替换掉,原来的值已经找不到了,所以再次赋值时并没有把第一个变量中
原来的值赋给第二个变量,而是将改变之后的值(即第二个变量的值)赋给了第二
个变量。正确的代码如下:

if m < n: # 比较大小,使得m中存储大数,n中存储小数
# 交换m和n的值
temp = m
m = n
n = temp

两个数的最大公约数有可能是其中较小的数,故在按从大到小的顺序寻找最大
公约数时,循环变量i的初值从较小的数n开始依次递减,去寻找第一个能同时整除
两个整数的自然数,并将其输出。需要注意的是,虽然判定条件是i>0,但在找到
第一个满足条件的i值后,循环没必要继续进行,如25和15,最大公约数是5,对于
后面的4、3、2、1便没必要再去执行,但此时判定条件仍然成立,要结束循环只能
借助break语句

# 按照从大到小的顺序寻找满足条件的自然
数
i = n
while i > 0:
if m % i == 0 and n % i == 0:
# 输出满足条件的自然数并结束
循环
print("%d 和 %d 的最大公约数
是: %d" %(m, n, i))
break
i -= 1

4.确定程序框架
程序的流程图如图4.8所示。

100个python算法超详细讲解:最大公约数_第1张图片

5.完整的程序
根据上面的分析,编写程序如下: 

#!/usr/bin/python3
# -*- coding: utf-8 -*-
# @author : liuhefei
最大公约数
# @desc: 最大公约数
if __name__ == "__main__":
	print("请输入两个整数")
	m = int(input("m = "))
	n = int(input("n = "))
	if m < n: # 比较大小,使得m中存储大数,n中存储小数
		# 交换m和n的值
		temp = m
		m = n
		n = temp
		i = n # 按照从大到小的顺序寻找满足条件的自然数
		while i > 0:
			if m % i == 0 and n % i == 0:
				# 输出满足条件的自然数并结束循环
				print("%d 和 %d 的最大公约数是: %d" %(m, n, i))
				break
				i -= 1### unterminated keywords

第一种思路,按照从小到大的顺序穷举两数公约数的程序代码如下:

#!/usr/bin/python3
# -*- coding: utf-8 -*-
# @author : liuhefei
# @desc: 最大公约数
if __name__ == "__main__":
print("请输入两个整数")
m = int(input("m = "))
n = int(input("n = "))
# 比较两个数的大小,进行交换
if m < n:
temp = n
n = m
m = temp
for i in range(1, n):
if m % i == 0 and n % i == 0:
k = i # 将当前情况下的最大公约数存储在k中
print("%d 和 %d 的最大公约数是:%d" %(m, n, k))

此算法第一步也需要对两个变量的值进行大小比较,使得变量m中存储大数,n
中存储小数。程序中需要注意的是,最后输出结果时不能直接输出变量i的值,因循
环结束时循环变量i的值为n,并不一定是要求的最大公约数。为了使求得的公约数
在穷举过程中被记录,可以将满足条件的i值暂存到变量k中,使得k中始终存储当前
情况下的最大公约数。
6.运行结果
在PyCharm下运行程序,屏幕上提示“请输入两个整数”。首先输入56和72,运
行结果如图4.9a所示,因为有可能两个数中较小的数正好是两数的最大公约数,故
再输入一组数8和4,运行结果如图4.9b所示。

100个python算法超详细讲解:最大公约数_第2张图片

 7.问题拓展
早在公元前300年左右,欧几里得就在他的著作《几何原本》中给出了求最大
公约数高效的解法——辗转相除法。辗转相除法用到的原理很简单,假设用f(x,y)表
示x和y的最大公约数,取k=x/y,b=x%y,则x=ky+b,如果一个数能够同时整除x和
y,则必能同时整除b和y;而能够同时整除b和y的数也必能同时整除x和y,即x和y
的公约数与b和y的公约数是相同的,其最大公约数也是相同的,则有
f(x,y)=f(y,x%y)(y>0),如此便可把原问题转换为求两个更小数的最大公约数,直
到其中一个数为0,剩下的另外一个数就是两者最大的公约数。
例如,12和30的公约数有:1、2、3、6,其中6就是12和30的最大公约数。
欧几里德算法,其思想可概括如下:
1)用较大的数m除以较小的数n,得到的余数存储到变量b中,b=m%n。
2)上一步中较小的除数n和得出的余数b构成新的一对数,并分别赋值给m和
n,继续做上面的除法。
3)若余数为0,其中较小的数(即除数)就是最大公约数。否则重复步骤1和
步骤2。
以求288和123的最大公约数为例,操作如下:
288÷123=2余42
123÷42=2余39
42÷39=1余3
39÷3=13
所以3就是288和123的最大公约数。
在进行辗转相除之前同样要确定两数中的大数和小数,将其分别存放在不同变
量中。
相应程序段如下:

#!/usr/bin/python3
# -*- coding: utf-8 -*-
# @author : liuhefei
# @desc: 最大公约数——辗转相除法
if __name__ == "__main__":
	print("请输入两个整数")
	m = int(input("m = ")) # m存储较大数
	n = int(input("n = ")) # n存储较小数
	print("%d 和 %d 的最大公约数是: " % (m, n), end="")
	# 比较两个数的大小,进行交换,使得m是最大数,n是最小数
	if m < n:
		temp = n
		n = m
		m = temp
		b = m % n # b存储m 和 n取模得到的余数
		while b != 0:
			m = n # 原来的小数作为下次运算时的大数
			n = b # 将上一次的余数作为下次相除时的小数
			b = m % n
			print("%d" %n)

在PyCharm下运行上面的程序,屏幕上提示“请输入两个整数”,输入245和87
后,结果为1,如图4.10a所示;输入78和102后,结果为6,如图4.10b所示。

100个python算法超详细讲解:最大公约数_第3张图片

8.拓展训练
求一个最小的正整数,这个正整数被任意n(2≤n≤10)除都是除不尽的,而且
余数总是n-1。例如,被9除时的余数为8。要求设计一个算法,不得使用枚举与“除
2,除3,…,除9,除10”有关的命令,求出这个正整数。 

你可能感兴趣的:(python,python算法,算法,python)