JavaScript中缺失的数学方法

在这篇文章中,我们将探讨一些在JavaScript中缺失的数学方法,并介绍如何为它们编写函数。

JavaScript的Math对象包含了一些非常有用和强大的数学操作,可以在Web开发中使用,但它缺少了许多其他大多数编程语言提供的重要操作(比如Haskell,它具有大量这样的操作)。

以下是每个操作的快速链接:

  • Sum

  • Product

  • Odd and Even

  • triangleNumber

  • Factorial

  • Factors

  • isPrime

  • Greatest Common Divisor

  • Lowest Common Multiple

JavaScript中缺失的数学方法:求和(Sum)

你可能还记得在学校中,“sum”是“add”的同义词。例如,如果我们对数字1、2和3求和,实际上是指1 + 2 + 3。

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

有两种编写这个函数的方式:我们可以使用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

sum([2,4,6,8]) === 20 // 2 + 4 + 6 + 8

能够对一组数字求和可能是JavaScript Math对象中最有用且最需要的“缺失”数学操作。再次强调,求和函数可以作为一个很好的检查工具。例如,在数独游戏中,我们可以通过检查列或行的总和是否为45(1 + 2 + 3 + 4 +…+ 9)来检查用户是否没有重复的数字。该函数在在线购物应用程序中也非常适用,如果我们想要计算总账单,假设所有价格都存储在一个数组中。

以下是在购物应用程序示例中如何使用该函数的示例代码:

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)
}

JavaScript中缺失的数学方法:乘积(Product)

我们的乘积函数将以与求和函数类似的方式工作,不同之处在于,我们将把列表中的所有数字相乘。

同样地,我们可以几乎与第一个求和函数一样使用for循环:

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

请注意,我们使用1来初始化total变量,而不是0,否则最后得到的乘积将始终为0。

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

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

以下是一些示例代码:

product([2,5,8,6]) === 480 // 2 x 5 x 8 x 6

product([3,7,10,2]) === 420 // 3 x 7 x 10 x 2

这个函数的用途可能看起来不明显,但在进行多个转换的计算时非常有用。例如,如果你想找出十个苹果包装袋(每袋一公斤,售价1.50RMB)的美元价格,而不是进行大量的乘法运算,将所有的值存储在一个数组中并使用刚刚编写的乘积函数将更加高效。

数组的一个示例格式如下:

const pricePerKg = 1.50
const numberOfKg = 10
const conversionRate = 1.16
const conversion = [1.50, 10, 1.16]

const USprice = product([pricePerKg,numberOfKg,conversionRate])

JavaScript中缺失的数学方法:奇数和偶数

这些函数将接受一个数字作为参数,可以是数组的长度,根据该数字是奇数还是偶数返回true或false。

要判断一个数字是否为偶数,它必须能够被2整除;而要判断一个数字是否为奇数,则相反,不能被2整除。这将是函数的关键部分。

例如,Haskell语言中内置了这些函数,这使得事情变得更加容易,尤其是可以直接编写如下代码:

even 29
<< false

odd 29
<< true

另一方面,Ruby提供了这些函数作为方法。这样编写起来仍然更加容易:

29.even?
<< false

29.odd?
<< true

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

11 % 3 === 2 // 11 divide 3 === 3 remainder 2

下面是我们的偶数函数的一个示例:

function even(number){
    return number % 2 === 0
}

正如我们所看到的,我们有一个接受数字作为参数的偶数函数,并根据条件返回一个布尔值:

number % 2 === 0

当这个数被2整除时,如果余数等于零,我们就知道它是可以被2整除的,函数将返回true。例如:

even(6) === true

even (9) === false

下面是我们的奇数函数的一个示例:

function odd(number){
    return number % 2 !== 0
}

这两个函数非常相似:都接受一个数字作为参数,并根据条件返回一个布尔值:

number % 2 !== 0

如果将数字除以2的余数不等于零,那么这个数就是奇数,函数将返回true。例如:

odd(7) === true

odd(114) === false

能够检查一个数字是奇数还是偶数非常重要,而且非常简单。一开始可能并不显得那么重要,但它可以作为一个很好的输入验证技术,例如对于数组长度,或者仅仅通过检查两个玩家游戏的胜者来判断。你可以追踪已经玩了多少回合,如果回合数是奇数,玩家1获胜,如果是偶数,玩家2获胜——假设第一回合计为1。

这两个函数是可以互换使用的,我们很可能只需要使用其中一个。然而,拥有这两个函数可以使在代码中更容易跟踪真值和假值逻辑,特别是在大块的代码中。

下面是我们如何编写上述示例的代码:

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

JavaScript中缺失的数学方法:三角数(Triangle Number)

三角数听起来比它们实际上要复杂得多。它们只是某个数之前所有整数的和。

例如,这是第五个三角数:5 + 4 + 3 + 2 + 1 = 15。

这与我们之前提到的数独的例子有关。我们想要检查所有数字是否唯一,我们可以通过检查它们是否与1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 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(7) === 28 // 0.5 x 7 x 8

triangleNumber(123) === 7626 // 0.5 x 123 x 124

JavaScript中缺失的数学方法:阶乘(Factorial)

一个自然数(大于0的任意整数)的阶乘是小于等于该数的所有数的乘积。例如:3的阶乘(表示为3!)是3 x 2 x 1 = 6。

与求和和乘积函数类似,我们可以用两种方式创建阶乘函数:使用for循环和使用递归。如果你之前没有接触过递归算法,它们本质上是反复调用自身的函数,直到达到一个“基本情况”。你可以在《函数式JavaScript中的递归》中了解更多关于它们的内容。

下面是我们如何使用for循环创建阶乘函数的方法:

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

该函数通过从1到给定的数字的循环遍历(每次递增)并将总数与每个数字相乘,最后返回最终的总数(即该数字的阶乘)。

下面是我们如何使用递归创建阶乘函数的方法:

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

在这个函数中,我们的基本情况是0,因为0的阶乘令人惊讶地是1(这个证明实际上非常有趣)。这意味着,当数字通过函数时,只要它不为零,它将乘以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

JavaScript中缺失的数学方法:因子(Factors)

因子是成对出现的,每一对因子相乘得到原始数字。例如:

  • 数字10的因子有:1和10;2和5。

  • 数字18的因子有:1和18;2和9;3和6。

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

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

首先,我们创建一个数组,初始为空。然后我们使用一个for循环来遍历从1到给定数字的每个整数,每次遍历时我们检查数字是否可被整数(或在这种情况下的计数器)整除。

如你所见,为了检查可被整除性,我们再次使用了模运算符。如果数字可被整数整除,则它是一个因子,并可以添加到我们的数组中。

然后返回该数组,每次运行函数时,将返回一个升序排列的因子数组。例如:

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

找出一个数字的因子在许多情况下非常有用,特别是当你需要组织分组时,比如在线游戏中,当你需要每个队伍都有相等数量的玩家。例如,如果你有20个玩家,每个队伍需要10个玩家,你可以使用因子函数将10个玩家分配到两个队伍中。同样地,如果每个队伍需要4个玩家,你可以使用因子函数将4个玩家分配到五个队伍中。

在实际应用中,可能如下所示:

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

JavaScript中缺失的数学方法:isPrime(是否为素数)

这是学校中最早学到的条件之一,但在日常生活中很少使用。简而言之,如果一个数有两个不同的因子,而这两个因子分别为1和它本身,那么这个数就是素数。素数从2开始:2、3、5、7、11、13、17、19......一直延伸到无穷大。

这个函数可能一开始看起来很复杂,但如果我们刚刚写过一个非常有用的因子函数,它实际上会变得非常简单。如前所述,如果一个数有两个不同的因子,那么它就是素数,因此我们的函数可以简单地写为:

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

该函数将根据因子列表的长度是否为2返回一个布尔值,换句话说,它将返回该数字是否有两个因子。

实际应用时,它会像这样:

isPrime(3) === true

isPrime(76) === false

isPrime(57) === true

在上面的“分组用户”示例中继续,如果用户数量是素数,我们无法平均分组(除非我们只有一个组,但这会违背示例的目的),这意味着我们必须等待另一个用户加入。因此,我们可以在这样的函数中使用它:

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

JavaScript中缺失的数学方法:gcd(最大公约数)

有时被称为“最高公因数”,最大公约数操作找到两个数共有的最大因数。

例如:

  • 12和15的最大公约数是3

  • 8和4的最大公约数是4。

一种简单的方法是列出每个数的所有因数(使用上面提到的令人难以置信的函数),然后比较这些列表。然而,比较这些列表需要一些巧妙但也不太高效的数组操作。

不过,下面仍然是一个示例:

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就是最大公约数。否则,最大公约数是number2和number1除以number2的余数的最大公约数。

同样,这两个函数都会返回相同的结果。例如:

gcd(24, 16) === 8

gcd(75, 1) === 1

最小公倍数(LCM)是找出两个或多个数能够整除的最小的正整数。

例如:

2和6的最小公倍数是6。 4和15的最小公倍数是60。 不幸的是,我们不能简单地列举出每个数的所有倍数,因为这将是一个无限列表。

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

(number1 x number2) / the Greatest Common Divisor of the two numbers

使用公式计算2和6的最小公倍数(LCM)的步骤如下:

  1. 找到两个数的最大公约数(GCD)。在这种情况下,2和6的最大公约数是2。
  2. 将两个数相乘并除以它们的最大公约数。在这种情况下,2乘以6除以2等于6。

因此,2和6的最小公倍数(LCM)是6。

(2 x 6)/gcd(2,6) = 12/2 = 6

幸运的是,我们刚刚创建了一个gcd函数,因此创建lcm函数非常容易:

function lcm(number1, number2){
    return (number1*number2)/gcd(number1, number2)
}

就这样!我们只需要返回上面的公式,它应该可以正常工作:

lcm(12, 9) === 36 // (12 x 9)/3

这个函数可能没有明显的实际用途,但在两个事件以不同的间隔发生时,我经常发现它非常有用。这意味着我们可以使用最小公倍数(LCM)来找出两个事件同时发生的时间。

例如,如果一个图像被设置为每隔六秒出现一次,而一个段落的文本被设置为每隔八秒出现一次,那么在第24秒时,图像和段落将首次同时出现。

全部评论

相关推荐

羊村懒哥:刚想骂一看是友军对不起
点赞 评论 收藏
分享
点赞 评论 收藏
分享
dongsheng66:如果想进大厂的话,在校经历没必要占这么大篇幅,可以把专业技能单独放一个专栏写,可以加个项目经历
点赞 评论 收藏
分享
10-07 20:48
门头沟学院 Java
不敢追175女神:可能是实习上着班想到后面还要回学校给导师做牛马,看着身边都是21-25的年纪,突然emo了了
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务