JavaScript修炼之路:递归运算 --- 欧几里得算法计算两个数的最大公约数

636 阅读4分钟

写在开头:
个人“才疏学浅”,小白一枚,下述内容均为学习过程中的笔记总结与个人感悟,若有错误之处敬请斧正

关于标题:标题取为修炼之路一是个人曾经是修仙类小说爱好者,二是偶然间看到过杨逸飞大神所著JavaScript百炼成仙一书有感而发


预备知识

最大公约数

最大公因数,也称最大公约数、最大公因子,指两个或多个整数共有约数中最大的一个。

欧几里得算法

定理:两个整数的最大公约数等于其中较小的那个数和两数相除余数的最大公约数。最大公约数(Greatest Common Divisor)缩写为GCD。

假如需要求 1997 和 615 两个正整数的最大公约数,用欧几里得算法,是这样进行的:

1997 / 615 = 3 (余 152)

615 / 152 = 4(余7)

152 / 7 = 21(余5)

7 / 5 = 1 (余2)

5 / 2 = 2 (余1)

2 / 1 = 2 (余0)

至此,最大公约数为1

以除数和余数反复做除法运算,当余数为 0 时,取当前算式除数为最大公约数,所以就得出了 1997 和 615 的最大公约数 1。

图片.png

以上解释参见此处百度百科原文

代码实现

基础逻辑版

function gcd(i,j) {
// 判断输入参数的大小,声明余数为res并计算余数大小
var res = Math.max(i,j) % Math.min(i,j);
// 判断余数是否为0,若为0则输出最小值参数
if (res === 0) return Math.min(i,j);
// 余数不为0递归计算直到余数为0结束函数
return res = gcd(Math.min(i,j),res); 
}
// 控制台输出最大公约数
console.log(gcd(24,36));

基础逻辑版优化

function gcd(i,j) {
// 声明并计算余数
var res = Math.max(i,j) % Math.min(i,j);
// 三目运算符判断余数是否为零,为0则输出最小数,不为零则将余数和最小数接着作为参数进行递归
return res === 0 ? Math.min(i,j) :res = gcd(Math.min(i,j),res);
}
console.log(gcd(115,22));

超强逻辑版

function gcd(m,n) {
// ???????????
return n ? gcd (n,m % n) : m
}
console.log(gcd(24,36));

图片.png

刚开始吴总和范总说人家大佬一行代码就解决了的时候我还不相信,我想了好久,这怎么也得两行代码吧...然后...就没有然后了,属实是鄙人太菜了 正常来讲,我想可能一般人都会陷入和我一样的误区,就是怎么说传进来的参数都应该要比较一下大小才能进行取余吧,那么就势必要进行判断,故而由此看不懂其代码。下面来理一下上述大佬一行代码为何能解决:

递归逻辑过程:

gcd(24,36); //gcd(m,n) -> return n ? gcd(n,m % n) : m

-> 36 ? gcd (36,24 % 36) : 24;                                        1

            -> 24 ? gcd (24,36 % 24) : 36;                           2

                        -> 12 ? gcd (12,24 % 12) : 24                3  

                                    -> 0 ? (条件为假,结束递归): 12;    4

Q1:为何上述代码无需比较两个参数的大小?
不知道大家是否还记得 % 运算有一句话(反正我是不记得了,记得也联想不到一块(小声哔哔))?
被除数模除数时,若被除数小于除数时,余数为自己本身

根据上述递归逻辑过程,由式1,我们故意将小数给m,大数给n,经过第一次调用,n判断为真执行递归,而里面的余数 m % n 在这里就起到了一个非常重要的作用,就是将小数重新赋值给下一次递归的n,故而他通过%的运算规则消除了参数大小顺序对函数带来的影响!!!

Q2:为何三目运算符的条件判断为n?
其实如果大家理解了开头预备知识里面的流程,我们就会知道,表面上是判断参数n,实际上,参数n在递归调用的过程中是充当的余数的身份的!所以此处判断应当解读为:判断余数是否为0!

Q3:为何三目运算符的执行语句中末尾假命题执行语句返回值为m?
这个问题与Q2类似,只需要明白m在递归过程中由于不断的赋值覆盖,其本质身份是每除(或者说模)一次的最小数,即最后一次取余得到余数为0时,此时的最小值就是开头传进来的两个数的最大公约数!

结束语

一直以来,我的逻辑思维始终停留在表层上,就像学习英语时,只会直译,并不会意译,但当深入了解内涵后,才真正体会到其中的美妙之处!


码字不易,如若对你有帮助,帮忙给个一键三连,孩子求求了🥺~