最大公约数:常用的四大算法求解最大公约数,分解质因数法、短除法、辗转相除法、更相减损法。

75 篇文章 0 订阅

常用的四大算法求解最大公约数,分解质因数法、短除法、辗转相除法、更相减损法。


(本文获得CSDN质量评分【91】)

【学习的细节是欢悦的历程】


  自学并不是什么神秘的东西,一个人一辈子自学的时间总是比在学校学习的时间长,没有老师的时候总是比有老师的时候多。
            —— 华罗庚


等风来,不如追风去……


常用的四大算法求解最大公约数
最大公约数
(分解质因数法、短除法、辗转相除法、更相减损法)


本文质量分:

91
本文地址: https://blog.csdn.net/m0_57158496/article/details/128977545

CSDN质量分查询入口:http://www.csdn.net/qc


在这里插入图片描述

目 录


◆最大公约数


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 不用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

        if c == 0: # 余数为0,终止while,返回结果。
            return b, int(product/b)

        b = a
        a = c

代码运行效果
图片描述

递归函数


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 “更相减损法”


  在求解最大公约数的几种方法中,辗转相除法最为出名。

辗转相除法与更相减损法的区别:

  • 都是求最大公因数的方法,计算上辗转相除法以除法为主,更相减损术以减法为主,计算次数上辗转相除法计算次数相对较少,特别当两个数字大小区别较大时计算次数的区别较明显。

  • 从结果体现形式来看,辗转相除法体现结果是以相除余数为0则得到,而更相减损术则以减数与差相等而得到。


回页目录

3、常用结论

  在解有关最大公约数、最小公倍数的问题时,常用到以下结论:

一、 如果两个自然数是互质数,那么它们的最大公约数是1,最小公倍数是这两个数的乘积。
例如8和9,它们是互质数,所以(8,9)=1,[8,9]=72。

二、 如果两个自然数中,较大数是较小数的倍数,那么较小数就是这两个数的最大公约数,较大数就是这两个数的最小公倍数。
例如18与3,18÷3=6,所以(18,3)=3,[18,3]=18。

三、 两个整数分别除以它们的最大公约数,所得的商是互质数。
例如8和14分别除以它们的最大公约数2,所得的商分别为4和7,那么4和7是互质数。

四、 两个自然数的最大公约数与它们的最小公倍数的乘积等于这两个数的乘积。
例如12和16,(12,16)=4,[12,16]=48,有4×48=12×16,即(12,16)×[12,16]=12×16。

五、 GCD(a,b) is the smallest positive linear combination of a and b.
a与b的最大公约数是最小的a与b的正线性组合,即对于方程ax+by=c来说,若a, b, x, y都为整数,那么c的最小正根为gcd(a,b)。



回页目录

4、参考资料



回页目录

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,穷举到了最后。


# while写法
def gcd(a, b):
    ''' “辗转相除法”求最大公约数和最小公倍数 '''
    product = a*b # 定义两数之积存储变量(计算最小公倍数用),后面的运算会改写a、b的值。
    
    while True:
        c = a%b

        if c == 0: # 余数为0,终止while,返回结果。
            return b, int(product/b)

        b = a
        a = c


# 递归写法
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()
    #input() 
    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博:

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

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

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

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

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

    打赏作者

    梦幻精灵_cq

    你的鼓励将是我创作的最大动力

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

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

    打赏作者

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

    抵扣说明:

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

    余额充值