JavaScript中不具备的数学函数,我们将探索 JavaScript 中缺少的一些数学函数,以及我们如何为它们编写函数。JavaScript Math 对象包含一些非常有用和强大的数学运算,可用于 Web 开发,但它缺少大多数其他语言提供的许多重要运算(例如 Haskell,它有大量的运算)。
JavaScript 中缺少数学方法:Sum
你可能还记得在学校,“sum”是“add”的同义词。例如,如果我们将数字 1、2 和 3 相加,它实际上意味着1 + 2 + 3
。
我们的sum
函数将涉及对数组中的所有值求和。
有两种写这个函数的方法:我们可以使用for
循环,或者我们可以使用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
能够对数字列表求和可能是 JavaScriptMath
对象中最有用和最需要的“缺失”数学运算。同样,sum
函数是一个很好的检查工具。例如,在数独游戏中,我们可以通过检查列/行加起来是否为 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
函数将以与函数类似的方式工作sum
,不同之处在于,我们不是将列表中的所有数字相加,而是将它们相乘。
再一次,我们可以使用与第一个函数for
几乎相同的循环:sum
function product(array){ let total = 1 for(let count = 0; count < array.length; count++){ total = total * array[count] } return total }
请注意,我们使用而不是初始化total
变量,否则我们将始终以 0 结尾。1
0
total
但该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.50 元),而不是使用巨大的乘法和,将所有值存储在一个数组中并使用product
我们刚刚编写的函数。
数组的一个例子是这种格式:
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
取决于数字是奇数还是偶数。
偶数必须能被二整除,奇数则相反,不能被二整除。这将是功能的关键部分。
例如,Haskell 内置了这些函数,这让事情变得容易多了,尤其是你可以这样写:
even 29 << false odd 29 << true
另一方面,Ruby 以方法的形式提供这些功能。这仍然更容易编写:
29.even? << false 29.odd? << true
在 JavaScript 中编写这些函数的最简单方法是使用余数运算符,%
. 当一个数字除以另一个数字时,这将返回余数。例如:
11 % 3 === 2 // 11 divide 3 === 3 remainder 2
这是我们的even
函数的示例:
function even(number){ return number % 2 === 0 }
如我们所见,我们有一个even
函数,它以一个数字作为参数并根据条件返回一个布尔值:
number % 2 === 0
当数字除以二时,如果余数等于零,我们知道它可以被二整除并true
返回。例如:
even(6) === true even (9) === false
这是我们的odd
函数的示例:
function odd(number){ return number % 2 !== 0 }
这两个函数非常相似:以一个数字作为参数,根据条件返回一个布尔值:
number % 2 !== 0
如果数字除以 2 的余数不等于零,则该数字为奇数,true
将被返回。例如:
odd(7) === true odd(114) === false
能够检查一个数字是奇数还是偶数是至关重要的,而且非常简单。起初它可能看起来并不那么重要,但它可以作为一种很好的输入验证技术——例如,使用数组长度,或者简单地检查两人游戏的获胜者。您可以跟踪已经玩了多少轮,如果数字是奇数,则玩家 1 获胜,如果是偶数,则玩家 2 获胜——假设第一轮计为 1。
这些功能是可以互换的,我们很可能只需要使用一个。然而,拥有这两个函数可以更容易地跟踪true
或false
逻辑,尤其是在大块代码中。
以下是我们如何对上面的示例进行编码:
function checkWinner(gamesPlayed){ let winner if(odd(gamesPlayed)){ winner = "player1" } else{ winner = "player2" } return winner }
JavaScript 中缺少数学方法:triangleNumber
三角形数字听起来比实际更花哨。它们只是直到某个数字的所有整数的总和。
例如,这是第五个三角形数: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 中缺少数学方法:阶乘
自然数(任何严格大于 0 的整数)的阶乘是所有小于或等于该数的数的乘积。例如:3阶乘(用 表示3!
)是3 x 2 x 1 = 6
。
与sum
andproduct
函数类似,有两种创建factorial
函数的方法:使用for
循环和使用递归。
以下是我们如何factorial
使用for
循环创建我们的函数:
function factorial(number){ let total = 1 for (let i = 1; i < number+1; i++){ total = total * i } return total }
此函数循环遍历从 1 到数字(每次递增)的所有数字,并将总数乘以每个数字,然后返回最终总数(数字阶乘)。
下面是我们如何factorial
使用递归创建我们的函数:
function factorial(number){ if (number <= 0){ return 1 } else{ return number * factorial(number - 1) } }
在这个函数中,我们的基本情况是零,因为0!
令人惊讶的是一(对此的证明实际上非常有趣)。这意味着,当数字通过函数时,只要它不为零,它就会乘以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 中缺少数学方法:因素
因子成对出现,每一对相乘形成原始数。例如:
- 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 到数字本身的每个整数,并且在每次通过时我们检查数字是否可以被整数整除(或者count
在这种情况下)。
如您所见,为了检查可分性,我们mod
再次使用符号。如果数字可以被整数整除,它就是一个因子,可以被推入我们的数组。
然后返回数组,每次运行该函数时,都会按升序返回一个因子数组。例如:
factors(50) === [1,2,5,10,25,50]
找到一个数字的因数可能非常有用,尤其是当您需要制定组时——例如在在线游戏中,当您需要每个团队中的用户数量相等时。例如,如果您有 20 个用户并且每个团队需要 10 个玩家,您可以使用factors
函数将这 10 个玩家与两个团队匹配。同样,如果每队需要四名球员,您可以使用该factors
功能将四人匹配成五队。
在实践中,它可能看起来像这样:
function createTeams(numberOfPlayers, numberOfTeams){ let playersInEachTeam if(factors(numberOfPlayers).includes(numberOfTeams)){ playersInEachTeam = numberOfPlayers / numberOfTeams } else{ playersInEachTeam = "wait for more players" } return playersInEachTeam }
JavaScript 中缺少数学方法:isPrime
这是您在学校学到的最早条件之一,但在日常生活中并不经常使用。简而言之,如果一个数有两个不同的因数,并且总是一个和它本身,那么这个数就是质数。质数开始:2、3、5、7、11、13、17、19 ……等等直到无穷大。
它最初可能看起来像一个复杂的函数——如果我们不只是编写了一个非常有用的函数,它可能确实如此factors
。如前所述,如果一个数有两个不同的因子,那么它就是质数,所以我们的函数就这么简单:
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 } }
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
(使用我们之前的函数)。如果 factors 数组number2
包含当前通道中的项目,我们将其推入我们的inCommon
数组。
一旦我们有了一个包含两个数字共有的所有因子的数组,我们就返回按降序排列的数组的第一个值。换句话说,我们返回最大公约数。
可以想象,如果我们还没有创建该factors
函数,那么为此编写的代码将会非常庞大。
一种更简洁但更难的方法是使用递归。这是一个非常著名的算法,称为欧几里德算法:
function gcd(number1, number2){ if(number2 === 0){ return number1 } else{ return gcd(number2, number1%number2) } }
我们这里的基本情况number2
等于 0,此时number1
是最大公约数。否则,GCD 是 GCD除以number2
的余数。number1
number2
同样,这两个函数将返回相同的东西。例如:
gcd(24, 16) === 8 gcd(75, 1) === 1
avaScript 中缺少数学方法:lcm(最小公倍数)
最小公倍数在与最大公约数相似的波长上工作,而是找到两个数字都是其因数的最小整数。
例如:
- 2和6的最小公倍数是6。
- 4和15的最小公倍数是60。
不幸的是,对于这个函数,我们不能只创建一个包含每个数字的所有倍数的数组,因为这将是一个无限列表。
但是,我们可以使用一个非常有用的公式来计算最小公倍数:
(number1 x number2) / the Greatest Common Divisor of the two numbers
要检查公式,您可以尝试使用上面的示例。2和6的LCM:
(2 x 6)/gcd(2,6) = 12/2 = 6
幸运的是,我们刚刚创建了一个gcd
函数,所以创建这个函数非常简单:
function lcm(number1, number2){ return (number1*number2)/gcd(number1, number2) }
就是这样!我们需要做的就是返回上面的公式,它应该可以工作:
lcm(12, 9) === 36 // (12 x 9)/3
这个函数可能没有任何明显的用途,但我经常发现它非常适合两个事件以不同的时间间隔发生的情况,这意味着我们可以使用 LCM 找出两个事件何时同时发生。
例如,如果一个图像被编程为每六秒出现一次,一段文本被编程为每八秒出现一次,那么图像和段落将在第 24 秒第一次同时出现。