JavaScript 中缺失的数学工具:Sum、Product、Prime 等实用函数实现
JavaScript 原生 Math 对象缺少许多基础数学操作。本文详细分享了在 JavaScript 中实现求和、乘积、奇偶判断、三角数、阶乘、因子分解、素数检测、最大公约数及最小公倍数等函数的方法,涵盖 for 循环与 reduce、递归等多种实现路径,并提供实际应用场景代码示例。
JavaScript 的 Math 对象提供了一些基础且强大的数学运算功能,适用于 Web 开发场景,但它并未涵盖其他多数编程语言(如 Haskell)所具备的大量高级数学操作。
下文将列出各个数学操作的快速链接:
- 求和(Sum)
- 乘积(Product)
- 奇数与偶数判断
- 三角数(Triangle Number)
- 阶乘(Factorial)
- 因子分解(Factors)
- 素数判断(isPrime)
- 最大公约数(Greatest Common Divisor)
- 最小公倍数(Lowest Common Multiple)
1. 求和(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)
}2. 乘积(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.50 RMB)的美元价格,无需进行繁琐的手动乘法运算,只需将所有相关数值存储于数组中,并调用刚才编写的乘积函数即可。
数组的一种示例格式如下:
const pricePerKg = 1.50
const numberOfKg = 10
const conversionRate = 1.16
const conversion = [1.50, 10, 1.16]
const USprice = product([pricePerKg,numberOfKg,conversionRate])3. 奇数和偶数
这些函数接受一个数字作为参数(可以是数组长度),并根据该数字是奇数还是偶数返回布尔值 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
}4. 三角数(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 1245. 阶乘(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 16. 因子(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
}7. 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
}
}8. 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)的步骤如下:
- 找到两个数的最大公约数(GCD)。在这种情况下,2 和 6 的最大公约数是 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 秒时,图像和段落将首次同时出现。
以上就是 JavaScript 中你不知道的数学方法分享(非常实用)的详细内容,更多关于 JavaScript 数学方法的资料请关注本站其它相关文章!
JS大数值转字符串出现科学计数法怎么办
2026-11-05 17:09:21
JS split切割后出现空字符串怎么过滤
2026-11-05 16:00:30
在 React 或 Vue 项目中封装使用 JS 节流防抖
2026-11-05 15:27:29
JavaScript中怎么将带小数的字符串转为整数?
2026-11-05 14:32:38
怎么用JavaScript策略模式重构电商促销计算逻辑?
2026-11-05 14:01:09
