Отсутствующие математические методы в JavaScript

Как стать разработчиком JavaScript Программирование и разработка

В этой статье мы рассмотрим некоторые из отсутствующих математических методов в JavaScript и то, как мы можем написать для них функции.

Объект JavaScript Math содержит несколько действительно полезных и мощных математических операций, которые можно использовать в веб-разработке, но в нем отсутствуют многие важные операции, которые предоставляет большинство других языков (таких как Haskell, в котором их огромное количество).

Отсутствующие математические методы в JavaScript: сумма

Вы, наверное, помните из школы, что «сумма» — это синоним «добавить». Например, если мы суммируем числа 1, 2 и 3, это действительно означает 1 + 2 + 3.

Наша sumфункция будет включать суммирование всех значений в массиве.

Есть два способа написать эту функцию: мы могли бы использовать forцикл или мы могли бы использовать reduceфункцию. Если вы хотите заново ознакомиться с этой reduceфункцией, вы можете прочитать об использовании map() и reduce() в JavaScript.

Использование 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

Возможность суммировать список чисел, пожалуй, самая полезная и самая необходимая «недостающая» математическая операция Mathобъекта JavaScript. Опять же, 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переменную с помощью 1вместо 0, так как в противном случае мы всегда получали бы значение total0.

Но 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

Если остаток от деления на два не равен нулю, то число нечетное и 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: Factorial

Факториал натурального числа (любое целое число, строго большее 0) — это произведение всех чисел, меньших или равных этому числу. Например: 3 факториала (обозначается 3!) есть 3×2 x 1 = 6.

Подобно функциям sumи product, существует два способа создания нашей factorialфункции: с помощью forцикла и с помощью рекурсии. Если вы раньше не встречались с рекурсивными алгоритмами, то они, по сути, представляют собой функции, которые многократно вызывают сами себя, пока не достигнут «базового случая». Подробнее о них можно прочитать в » Рекурсия в функциональном JavaScript «.

Вот как мы можем создать нашу 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: 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 до самого числа, и при каждом проходе мы проверяем, делится ли число на целое число (или 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 (наибольший общий делитель)

Операция поиска наибольшего общего делителя, иногда известная как «наибольший общий делитель», находит наибольший общий делитель двух чисел.

Например:

  • The GCD of 12 and 15 is 3.
  • The GCD of 8 and 4 is 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

Отсутствующие математические методы в JavaScript: lcm (Lowest Common Multiple)

Наименьшее общее кратное работает с той же длиной волны, что и наибольший общий делитель, но вместо этого находит наименьшее целое число, делителями которого являются оба числа.

Например:

  • LCM 2 и 6 равно 6.
  • LCM 4 и 15 равен 60.

К сожалению, для этой функции мы не можем просто создать массив всех кратных каждому числу, так как это был бы бесконечный список.

Однако есть очень полезная формула, которую мы можем использовать для вычисления наименьшего общего кратного:

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

Чтобы проверить формулу, вы можете попробовать ее на примере выше. LCM 2 и 6:

(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-й секунде.

Заключение

Все вышеперечисленные функции можно найти в следующей демонстрации CodePen, где вы можете взаимодействовать с функциями и увидеть их работу на практике.

Однако, если вы хотите избавить себя от копирования этих функций каждый раз, когда они вам понадобятся, я скомпилировал их (плюс несколько других) в мини-библиотеку под названием JOG-Maths.

Надеюсь, это дало вам некоторые идеи о том, какие математические операции вы можете использовать помимо встроенного Mathобъекта JavaScript и мощности математики в коде!

Читайте также:  Как выполнить анализ данных в Python с использованием API OpenAI
Оцените статью
bestprogrammer.ru
Добавить комментарий