五十6、从高中碾转相除法、更相减损术算法谈起

图片

「@Author:Runsen」git

编程的本质来源于算法,而算法的本质来源于数学,编程只不过将数学题进行代码化。「---- Runsen」github

先问大家一个小学问题:「如何求两个整数的最大公约数?」面试

曾经见过很多的算法题,发现有的并不在数据结构和算法大纲中,而是来源于高中数学。算法

高中数学在必修三中,有一个很是重要的知识点,叫作「碾转相除法、更相减损术」编程

展转相除法, 又名欧几里德算法(Euclidean algorithm)乃求两个正整数之最大公因子的算法。它是已知最古老的算法, 其可追溯至公元前300年前。微信

在古代,有一个比较出名的数学家,叫作「刘徽」。而更相减损术是我国数学家刘徽的专著《九章算术》中记载的.数据结构

碾转相除法

展转相除是求最大公约数的一种算法。给两个数,咱们能够把它组成数对(a,b)数据结构和算法

展转相除法基于以下原理:「两个整数的最大公约数等于其中较小的数和两数的相除余数的最大公约数。」ide

求a和b的最大公约数,就用ab中较小的数去除另外一个数,这个时候会有一个余数,当余数是0的时候,那个较小的数就是最大公约数。idea

若余数不是0,那么咱们用这个余数来替换那个比较大的数,而后以此类推,直到算出最大公约数。

好比,下面我用碾转相除法求100和24的最大公约数,很明显最大公约数就是25。

100 = 24 * 4  + 4
24  =  4 * 6  + 0 

很显然46中,那个较小的数4就是10024最大公约数。

下面用碾转相除法求55和120的最大公约数,很明显最大公约数就是5。

55 = 120 * 0  + 55
120  =  55 * 2  + 10
55 = 10 * 5 + 5

很显然105中,那个较小的数5就是55120最大公约数。

图片算法的流程图(摘自百度百科)

所以获得设两数为m,n,这里不须要判断两数中谁最大。

求m,n两数的最大公约数的步骤为:

用m除以n,m%n=r(r>=0)。若是r=0,则min(m,n)

若是r≠0,用n除以r,依此循环,直到r=0结束

下面,咱们将使用对碾转相除法进行代码化。

def gcd(a, b):
    # 若是b是0,退出循环
    while b:
        # 循环赋值
        a, b = b, a%b
    return a
print(gcd(100,25)) #25

展转相除法本质上是一种递归的代码,把求两个大数的公约数gcd(a,b)转化为 求其中较小的数和两数的相除余数的最大公约数gcd(b,a%b),直至b为0,则返回a为求得的最大公约数gcd(gcd(a,b), 0)

所以能够获得:gcd(a,b) = gcd(b,a%b) = gcd(gcd(a,b), 0)

def gcd(a, b): 
    return gcd(b, a % b) if b != 0 else a
    
print(gcd(55,120)) #5

下面对Python代码进行Java的代码转化

/**
 * @author Runsen
 * @date 2020/12/9 13:18
 */

public class Gcd {
    public static void main(String[] args) {
        int gcd = gcd(9149);
        System.out.println(gcd);
    }

    private static int gcd(int a, int b) {
        while(b != 0) {
            int temp = a % b;
            a = b;
            b = temp;
        }
        return a;
    }
}

下面对Python代码进行JavaScript的代码转化。

function gcd(a, b){
    while(b != 0){
       temp = a % b;
       a = b;
       b = temp;
    };
    return a;
}
     
console.log((gcd(55,120))) #5

更相减损术

我国早期也有求最大公约数问题的算法,就是更相减损术。

在《九章算术》中有更相减损术求最大公约数的步骤:可半者半之,不可半者,副置分母子之数,以少减多,更相减损,求其等也,以等数约之。

更相减损术来源于数的整除性质:即若是两个整数a、b都能被c整除,那么a与b的差也能被C整除。

好比求98和63的最大公约数。

先看98和63这两个数,由于63不是偶数,因此用大数减去小数,获得98-63=35 , 63-35=28 35-28=7 , 28-7=21 , 21-7=14 , 14-7=7 。

「此时,减数和差相等7」,因此98和63的最大公约数是7。

再好比求260和104的最大公约数。

先看260和104两个数,这两个数都是偶数,因此用2约简得130和52。

约简以后的130和52也都是偶数,继续用2约简得65和26,此时65不是偶数,因此用大数减去小数 65-26=39 , 39-26=13 , 26-13=13

此时,减数和差相等,再上面约去2个2, 获得的数是13,因此260和104的最大公约数是2×2×13=52。

所以更相减损术不在以下:

  • 若是两个整数都是偶数,就使用2约简,直到两个整数再也不都是偶数,而后执行第2步。若是两个整数不都是偶数,则直接执行第2步。
  • 用较大的数减去较小的数,若是获得的差刚好等于较小的数,则中止。不然,对较小的数和差值重复这个过程。
  • 第1步中约掉的若干个2和第2步中获得的差的乘积为原来两个整数的最大公约数。
图片

下面,咱们将使用对更相减损术进行代码化。

'''
@Author:Runsen
@WeChat:RunsenLiu
@微信公众号:Python之王
@CSDN:https://blog.csdn.net/weixin_44510615
@Github:https://github.com/MaoliRUNsen
@Date:2020/12/9
'''

def MaxCommDivisor(m, n):
    # 若是两个整数都是偶数,就使用2约简,须要记录约简次数
    index = 1
    while m % 2 == 0 and n % 2 == 0:
        m = m / 2
        n = n / 2
        index = index * 2
    # 用较大的数减去较小的数,所以须要判断m和n的大小,确保m是最大的。
    if m < n:
        m, n = n, m
    # 用较大的数减去较小的数,若是获得的差刚好等于较小的数,则中止。不然,对较小的数和差值重复这个过程。
    while m - n != n:
        diff = m - n
        if diff > n:
            m = diff
        else:
            m = n
            n = diff
    return n * index

print(MaxCommDivisor(2412)) #12

更相减损术和展转相除法在一千多年前的东方和西方同时被提出,这说明天才的想法老是惊人的类似,人类科技文明的进程也是同步的,这就是算法之美。

本文已收录 GitHub,传送门~[1] ,里面更有大厂面试完整考点,欢迎 Star。



Reference

[1]

传送门~:https://github.com/MaoliRUNsen/runsenlearnpy100




- END -

图片

相关文章
相关标签/搜索