JavaScript 中缺失的数学方法

276 阅读6分钟

前言

在本文中,我们将探讨 JavaScript 中缺少的一些数学方法,以及如何编写函数实现他。

JavaScript Math 对象包含一些非常有用和强大的数学运算,可用于 Web 开发,但它缺少大多数其他语言提供的许多重要运算。我们将讲解9个数学方法

  • Sum 求和
  • Product 求积
  • Odd and Even  求奇数和偶数
  • triangleNumber  三角形数
  • Factorial 阶乘
  • Factors因素
  • isPrime求质数
  • Greatest Common Divisor 最大公约数
  • Lowest Common Multiple 最小公倍数

Sum 求和

例如,如果我们将数字 1、2 和 3 相加,它实际上意味着 1 + 2 + 3 。

我们的 sum 函数将涉及对数组中的所有值求和。

有两种编写此函数的方法:我们可以使用 for 循环,或者我们可以使用 reduce 函数。如果您想重新熟悉 reduce 函数,可以阅读有关在 JavaScript 中使用 map() 和 reduce() 的内容。

使用 for 循环:

function sum(array){
    let total = 0
    for(let count = 0; count < array.length; count++){
        total = total + array[count]
    }
    return total
}

使用 reduce 函数:

function sum(array){
    return array.reduce((sum, number) => sum + number, 0)
}

这两个函数的工作方式完全相同( reduce 函数本质上只是一个内置的 for 循环),并且将返回相同的数字(给定相同的数组)。但是 reduce 函数要简洁得多。

所以,调用函数:

sum([1,2,3,4]) === 10 // 1 + 2 + 3 + 4

能够对数字列表求和可能是 JavaScript Math 对象中最有用和最需要的数学运算。同样, sum 函数是一个很好的检查工具。

例如,数独游戏中我们可以通过检查列/行加起来是否为 45 (1 + 2 + 3 + 4 +...+ 9) 来检查用户是否在该列或行中有没有重复。

例如商城系统,如果我们想计算总账单,将所有价格都存储在一个数组中,用该函数计算就很 nice

示例:

const prices = [2.80, 6.10, 1.50, 1.00, 8.99, 2.99]

function totalCost(prices){
    return prices.reduce((sum, item) => sum + item, 0)
}

Product (求积)

我们的 product 函数与 sum 类似,不同之处在于,我们不是将列表中的所有数字相加,而是将它们相乘。

我们可以使用与第一个 sum 函数几乎相同的 for 循环:

function product(array){
    let total = 1
    for(let count = 0; count < array.length; count++){
        total = total * array[count]
    }
    return total
}

请注意,我们使用 1 而不是 0 初始化 total 变量,否则我们将始终得到 0 。

reduce 函数在这种情况下仍然有效,并且仍然是一种更简洁的函数编写方式:

function product(array){
    return array.reduce((total, num) => total*num, 1)
}

例如:

product([2,4,6,8]) === 768 // 2 x 4 x 6 x 8

Odd and Even 判断奇数、偶数

这些函数将接受一个数字,并根据数字是奇数还是偶数返回 true 或 false 。

JavaScript 中编写这些函数的最简单方法是使用余数运算符 % 。当一个数字除以另一个数字时,这将返回余数。例如:

11 % 3 === 2 // 11 % 3 === 3 余数 2

even 函数的示例:

function even(number){
    return number % 2 === 0
}
// 当数字除以二时,如果余数等于零,我们知道它可以被二整除, `true` 将被返回。例如:
even(4) === true

even (5) === false

odd 函数的示例:

function odd(number){
    return number % 2 !== 0
}
// 如果数除以 2 的余数不为零,则该数为奇数,将返回 `true` 。例如:
odd(3) === true

odd(14) === false 

能够检查一个数字是奇数还是偶数是至关重要的,而且非常简单。起初它可能看起来并不那么重要,但它可以作为一种很好的输入验证。

例如,使用数组长度,或者简单地检查两人游戏的获胜者。您可以跟踪已经玩了多少轮,如果数字是奇数,则玩家 1 获胜,如果是偶数,则玩家 2 获胜——假设第一轮计为 1。

function checkWinner(gamesPlayed){
    let winner
    if(odd(gamesPlayed)){
        winner = "player1"
    }
    else{
        winner = "player2"
    }
    return winner
}

这些功能是可以互换的,我们很可能只需要使用一个。但是,拥有这两个函数可以更容易地跟踪 true 或 false 逻辑,尤其是在复杂的代码中。

triangleNumber 三角形数

三角形数是特定数字之前所有整数的总和。

例如,这是 5 的三角形数:5 + 4 + 3 + 2 + 1 = 15

我们之前的数独示例。我们要检查所有数字是否唯一,我们可以通过检查它们是否匹配 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 的结果来做到这一点。当然,这是 9 的三角形数!

当然,我们可以使用 for 循环编写函数,如下所示:

function triangleNumber(number){
    let sum = 0
    for(let i=1; i < number + 1; i++){
        sum = sum + i
    }
    return sum
}

然而,这将是一个非常低效的函数,因为有一个非常简单的三角形数计算公式:

0.5 x (number) x (number + 1) 

function triangleNumber(number){
    return 0.5 * number * (number + 1)
}
triangleNumber(9) === 28 // 0.5 x 9 x 10

Factorial 阶乘

自然数(任何严格大于 0 的整数)的阶乘是所有小于或等于该数的数的乘积。例如:3 阶乘(由 3! 表示)是 3 x 2 x 1 = 6 。

与 sum 和 product 函数类似,有两种方法可以创建我们的 factorial 函数:

使用 for 循环或者使用递归。递归算法本质上是反复调用自身直到达到基数的函数。

以下是我们如何使用 for 循环创建我们的 factorial 函数:

function factorial(number){
  let total = 1
  for (let i = 1; i < number+1; i++){
    total = total * i
  }
  return total
}

此函数循环遍历从 1 到number(每次递增)的所有数字,并将总数乘以每一项,然后返回最终总数(数字阶乘)。

下面是我们如何使用递归创建我们的 factorial 函数:

function factorial(number){
  if (number <= 0){
    return 1
  }
  else{
    return number * factorial(number - 1)
  }
}

在这个函数中,我们的基数为零,因为 0! === 1 。这意味着,当number通过函数时,只要它不为零,它就会乘以 factorial(number - 1) 。

为了帮助准确理解此函数在每次传递时的作用,跟踪算法可能会有所帮助。这是用 3 跟踪的算法:

factorial(3) === 3*factorial(2) === 3*2*factorial(1) === 3*2*1*factorial(0) === 3*2*1*1 === 3*2*1 === 6

无论哪种方式,这两个函数都将返回相同的值。例如:

factorial(5) === 120 // 5 x 4 x 3 x 2 x 1

Factors 因素

因素成对出现,每一对相乘形成原始数。例如:

  • 10的因数是:1和10; 2 和 5。
  • 18的因数是:1和18; 2 和 9; 3 和 6。

我们希望我们的 factors 函数接受一个数字,并返回一个包含其所有因子的数组。这个函数的写法有很多种,但最简单的方法是使用命令式的方法,例如:

function factors(number){
    let factorsList = []
    for(let count = 1; count < number+1; count++){
        if(number % count === 0){
            factorsList.push(count)
        }
    }
    return factorsList
}

首先,我们创建一个空数组。然后我们使用 for 循环遍历从 1 到number的每个整数,并且在每次循环时我们检查数字是否可以被整数(在本例中为 count )整除。如果数字可以被整数整除,它就是一个因子,可以被推入我们的数组

然后返回数组,每次运行该函数时,都会按升序返回一个因子数组。例如:

factors(50) === [1,2,5,10,25,50]

找到一个数字的因数可能非常有用,例如在线游戏中,当您需要每个团队中的用户数量相等时。如果您有 20 个玩家并且每个团队需要10 个玩家,您可以使用 factors 函数计算出2个队伍。同样,如果每队需要四名球员,您可以使用 factors 函数计算出5支队伍。

function createTeams(numberOfPlayers, numberOfTeams){
    let playersInEachTeam
    if(factors(numberOfPlayers).includes(numberOfTeams)){
        playersInEachTeam = numberOfPlayers / numberOfTeams
    }
    else{
        playersInEachTeam = "wait for more players"
    }
    return playersInEachTeam
}

isPrime 求质数

这是您在学校学到的最早条件之一,但在日常生活中并不经常使用。简而言之,如果一个数有两个不同的因数,并且总是 1 和它本身,那么这个数就是 质数 。例如:2、3、5、7、11、13、17、19 ……等等。

如果一个数有两个不同的因子,那么它就是质数,所以:

function isPrime(number){
    return factors(number).length === 2
}

这将根据其因子列表的长度是否为二返回一个布尔值——换句话说,它是否有两个因子。

在实践中,它看起来像这样:

isPrime(3) === true

isPrime(76) === false

isPrime(57) === true

分组用户示例,如果用户的数量是质数,我们不能将它们平均分组(除非我们只有一组),这意味着我们将等待另一个用户加入。所以,我们可以在这样的函数中使用它:

function addUsers(users){
    if(isPrime(users)){
        wait = true
    }
    else{
        wait = false
    }
}

gcd (Greatest Common Divisor) 最大公约数

有时被称为最大公因数,最大公因数是找到两个数共享的最大因数。

 例如:

  • 15 和 18 的 GCD 是 3。
  • 4 和 6 的 GCD 是 2。

解决这个问题的一个简单方法是列出每个数字的所有因数(使用我们上面incredible函数)并比较这些列表。然而,比较列表需要一些非常漂亮但效率低下的数组操作。

比如:

function gcd(number1, number2){
    let inCommon = []
    for(let i of factors(number1)){
        if(factors(number2).includes(i)){
            inCommon.push(i)
        }
    }
    return inCommon.sort((a,b)=> b - a)[0]
}

在这里,我们将一个空数组分配给变量 inCommon 并循环遍历 number1 的因子数组(使用我们之前的函数)。如果 number2 的因子数组包含当前通道中的项目,我们将其推入我们的 inCommon 数组。

一旦我们有了一个包含两个数字共有的所有因子的数组,我们就返回按降序排列的数组的第一个值。换句话说,我们返回最大公约数。

可以想象,如果我们还没有创建 factors 函数,那么这方面的代码将会非常庞大。

一种更简洁但更难的方法是使用递归。这是一个非常著名的算法,称为 欧几里德算法: :

function gcd(number1, number2){
    if(number2 === 0){
        return number1
    }
    else{
        return gcd(number2, number1%number2)
    }
}

我们这里的基本情况是 number2 等于 0,此时 number1 是最大公约数。否则,GCD 是 number2 的 GCD 和 number1 的余数除以 number2 。

同样,这两个函数将返回相同的东西。例如:

gcd(24, 16) === 8

gcd(75, 1) === 1

lcm (Lowest Common Multiple)最小公倍数

最小公倍数在与最大公约数相似,找到两个数字都是其因数的那个最小整数

 例如:

  • 2 和 6 的 LCM 是 6。
  • 4 和 15 的 LCM 是 60。

我们不能只创建一个包含每个数字的所有倍数的数组,因为这将是一个无限列表。

但是,我们可以使用一个非常有用的公式来计算最小公倍数:

(number1 x number2) / 两个数的最大公约数

(3 x 6)/gcd(3,6) = 18/3 = 6

我们刚刚创建了一个 gcd 函数,因此创建这个函数非常简单:

function lcm(number1, number2){
    return (number1*number2)/gcd(number1, number2)
}
lcm(12, 9) === 36 // (12 x 9)/3

我们可以使用 LCM 找出两个事件在什么时候同时发生。

例如,如果一个图像 每六秒出现一次,一段文本 每八秒出现一次,那么图像和文本将在第 24 秒第一次同时出现。

总结

希望这能让您了解除了内置 JavaScript Math 对象之外

还可以使用哪些数学运算以及代码中数学的强大功能!

全文完。

谢谢!

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 5 天

点击查看活动详情