Автор оригинала: Jackson Bates.
Пару недель назад, туризм начал неофициальный алгоритм конкуренции на Бесплатный форум Code Camp Отказ
Задача казалась достаточно простой: вернуть сумму всех кратных 3 или 5, которые находятся ниже ряд n, где n представляет собой входной параметр к функции.
Но вместо того, чтобы просто найти какое-либо решение, конкуренция P1xT требовала вас сосредоточиться на эффективности. Он призвал вам написать свои собственные тесты и проводить представление о производительности ваших решений.
Это разбивка каждой функции, которую я пробовал и проверил, включая мои тесты и тестовые сценарии. В конце я покажу функцию, которая взорвала все мои собственные из воды, и научила меня ценный урок.
Функция № 1: массив, толкание, приращение
function arrayPushAndIncrement(n) {
var array = [];
var result = 0;
for (var i = 1; i < n; i ++) {
if (i % 3 == 0 || i % 5 == 0) {
array.push(i);
}
}
for (var num of array) {
result += num;
}
return result;
}
module.exports = arrayPushAndIncrement; // this is necessary for testingДля таких проблем, как это, мой мозг по умолчанию: построить массив, затем сделайте что-нибудь в этот массив.
Эта функция создает массив и толкает любые номера, которые соответствуют нашему условию (делится на 3 или 5). Затем он проводит сквозь этот массив, добавляя все значения вместе.
Настройка тестирования
Вот автоматизированные тесты для этой функции, которые используют Mocha и Chai, работающие на Nodejs.
Если вы хотите больше информации о установке Mocha и Chai, я написал Подробное руководство На форуме Free Code Camp.
Я написал простой тестовый скрипт, использующий значения P1XT. Обратите внимание, что в скрипте ниже функция включена в виде модуля:
// testMult.js
var should = require( 'chai' ).should();
var arrayPushAndIncrement = require( './arrayPushAndIncrement' );
describe('arrayPushAndIncrement', function() {
it('should return 23 when passed 10', function() {
arrayPushAndIncrement(10).should.equal(23);
})
it('should return 78 when passed 20', function() {
arrayPushAndIncrement(20).should.equal(78);
})
it('should return 2318 when passed 100', function() {
arrayPushAndIncrement(100).should.equal(2318);
})
it('should return 23331668 when passed 10000', function() {
arrayPushAndIncrement(10000).should.equal(23331668);
})
it('should return 486804150 when passed 45678', function() {
arrayPushAndIncrement(45678).should.equal(486804150);
})
})Когда я провел тест, используя Mocha testmult.js Это вернуло следующее:
Для всех будущих функций в этой статье предположим, что они передали все тесты. Для вашего собственного кода добавьте тесты для каждой новой функции, которую вы пытаетесь.
Функция № 2: массив, толчок, уменьшить
function arrayPushAndReduce(n) {
var array = [];
for (var i = 1; i < n; i ++) {
if (i % 3 == 0 || i % 5 == 0) {
array.push(i);
}
}
return array.reduce(function(prev, current) {
return prev + current;
});
}
module.exports = arrayPushAndReduce;Таким образом, эта функция использует аналогичный подход к моему предыдущему, но вместо использования для Цикл, чтобы построить окончательную сумму, она использует Fancier Уменьшить метод.
Настройка тестирования производительности
Теперь, когда у нас есть две функции, мы можем сравнить их эффективность. Опять же, благодаря P1xt для предоставления этого сценария в предыдущем потоке форума.
// performance.js
var Benchmark = require( 'benchmark' );
var suite = new Benchmark.Suite;
var arrayPushAndIncrement = require( './arrayPushAndIncrement' );
var arrayPushAndReduce = require( './arrayPushAndReduce' );
// add tests
suite.add( 'arrayPushAndIncrement', function() {
arrayPushAndIncrement(45678)
})
.add( 'arrayPushAndReduce', function() {
arrayPushAndReduce(45678)
})
// add listeners
.on( 'cycle', function( event ) {
console.log( String( event.target ));
})
.on( 'complete', function() {
console.log( `Fastest is ${this.filter( 'fastest' ).map( 'name' )}`);
})
// run async
.run({ 'async': true });Если вы запустите это с Узел Performance.js Вы увидите следующий вывод терминала:
arrayPushAndIncrement x 270 ops/sec ±1.18% (81 runs sampled) arrayPushAndReduce x 1,524 ops/sec ±0.79% (89 runs sampled) Fastest is arrayPushAndReduce
Так что используя Уменьшить Метод дал нам функцию, которая была 5 раз быстрее !
Если это не поощряет достаточно, чтобы продолжить больше функций и тестирования, я не знаю, что такое!
Функция № 3: В то время как массив, уменьшить
Сейчас я всегда добираюсь до надежды для петля, я подумал, что проверил в то время как Контур альтернативы:
function whileLoopArrayReduce(n) {
var array = [];
while (n >= 1) {
n--;
if (n%3==0||n%5==0) {
array.push(n);
}
}
return array.reduce(function(prev, current) {
return prev + current;
});
}
module.exports = whileLoopArrayReduce;И результат? Крошечный бит медленнее:
whileLoopArrayReduce x 1,504 ops/sec ±0.65% (88 runs sampled)
Функция № 4: В то время как, сумма, нет массивов
Итак, нахождение того, что тип цикла не имел огромное значение, мне интересно, что произойдет, если бы я использовал метод, который избегал массивов:
function whileSum(n) {
var sum = 0;
while (n >= 1) {
n--;
if (n%3==0||n%5==0) {
sum += n;
}
}
return sum;
}
module.exports = whileSum;Как только я начал думать по этому пути, это заставило меня понять, насколько я не прав, для всегда Добраться от массивов первым …
whileSum x 7,311 ops/sec ±1.26% (91 runs sampled)
Еще одно массовое улучшение: почти 5 раз быстрее Опять же, и 27 раз быстрее чем моя оригинальная функция!
Функция № 5: для, сумма
Конечно, мы уже знаем, что для петли должен быть немного быстрее:
function forSum(n) {
n = n-1;
var sum = 0;
for (n; n >= 1 ;n--) {
(n%3==0||n%5==0) ? sum += n : null;
}
return sum;
}Это использует Ternary Operator для проверки состояния, но мое тестирование показало, что нетрарная версия этого является той же, производительностью.
forSum x 8,256 ops/sec ±0.24% (91 runs sampled)
Итак, немного быстрее снова.
Моя окончательная функция закончилась быть 28 раз быстрее чем мой оригинал.
Я чувствовал себя как чемпион.
Я был над луной.
Я отдыхал на лаврах.
Введите математику
Неделя прошла, и последние решения от всех были размещены, проверены и сопоставлены. Функция, которая выполнила самые быстрые петель, и использовала алгебраическую формулу для хрустации чисел:
function multSilgarth(N) {
var threes = Math.floor(--N / 3);
var fives = Math.floor(N / 5);
var fifteen = Math.floor(N / 15);
return (3 * threes * (threes + 1) + 5 * fives * (fives + 1) - 15 * fifteen * (fifteen + 1)) / 2;
}
module.exports = multSilgarth;Ждать его…
arrayPushAndIncrement x 279 ops/sec ±0.80% (83 runs sampled) forSum x 8,256 ops/sec ±0.24% (91 runs sampled) maths x 79,998,859 ops/sec ±0.81% (88 runs sampled) Fastest is maths
Самый быстрый – математика
Так что выигрышная функция была примерно 9,690 раз быстрее чем мои лучшие усилия, и 275 858 раз быстрее чем мои первоначальные усилия.
Если вам нужна, я буду закончиться в Ханской академии, изучающей математику.
Спасибо всем, что участвовали, и поделились своими решениями в духе помощи другим туристам изучать новые методы.
Если вам интересно, вот запись P1XT на соревнованиях, а также все данные тестирования и тестирования:
P1XT/ALGO-OCT-17 Algo-Oct-17 – JavaScript Algorithm Challenge – 9 по 16 октября github.com.