前言
在本文中,我们将探讨 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 天