Исходя из тегов в вашем вопросе, я предполагаю, что вы хотите улучшить производительность этого кода.
Вот некоторые методы, которые вы можете использовать, более или менее отсортированные по приросту производительности, который они принесут в ваш код, за исключением первого, но я рекомендую вам реализовать их один за другим в своем коде и посмотреть, как они влияют на его производительность по отдельности.
✏️ Основы:
Во-первых, как Sterling Archer предложил в комментарии, вы должны использовать локальные переменные вместо глобальных, поэтому добавьте let
перед ними.
прирост производительности для выполненияэто, вероятно, можно пренебречь, но это не единственная причина изменить его: использование глобальных переменных считается плохой практикой, поскольку они загрязняют глобальное пространство имен и усложняют поддержание вашего кода: Почему глобальные переменные считаются плохой практикой?
⛓️ Запоминание экспоненты:
Вместо выполнения Math.pow(1.2, b + i)
на каждой итерации, которую вы можетевычислите текущее значение итерации, используя предыдущее, так как умножение должно быть значительно быстрее возведения в степень:
let exp = Math.pow(1.2, b);
for (let i = 1; i < c; ++i) {
exp *= 1.2;
rv += Math.floor(a * exp);
}
Этот метод называется memoization .
? Floorс побитовым оператором NOT (~):
Если a
положителен, а значения, которые вы используете, всегда равны < 2147483648
, вы можете использовать побитовое НЕ (~
) Math.floor()
, как вы можете видеть здесь:
console.log(`Math.floor(4.01) = ${ Math.floor(4.01) }`);
console.log(`Math.floor(4.99) = ${ Math.floor(4.99) }`);
console.log(`Math.floor(-4.01) = ${ Math.floor(-4.01) }`);
console.log(`Math.floor(-4.99) = ${ Math.floor(-4.99) }`);
console.log(`~~4.01 = ${ ~~4.01 }`);
console.log(`~~4.99 = ${ ~~4.99 }`);
console.log(`~~-4.01 = ${ ~~-4.01 }`);
console.log(`~~-4.99 = ${ ~~-4.99 }`);
console.log(`Math.floor(2147483647.99) = ${ Math.floor(2147483647.99) }`);
console.log(`Math.floor(2147483648.01) = ${ Math.floor(2147483648.01) }`);
console.log(`~~2147483647.99 = ${ ~~2147483647.99 }`);
console.log(`~~2147483648.01 = ${ ~~2147483648.01 }`);
.as-console-wrapper {
max-height: 100vh !important;
}
Однако, если вы попытаетесь найти значение >= 2147483648
, ~~
обернет и вернет неправильное значение, так как побитовые операторы работают с 32-битнымицелые числа, поэтому максимальное значение, которое вы можете безопасно использовать, составляет 2 31 -1 или 2147483647
.
? Этаж с побитовыми операторами альтернативный:
В этом случаевы можете использовать Math.trunc()
вместо Math.floor()
, что немного быстрее, как вы можете видеть здесь: https://jsperf.com/number-truncating-methods/1
? Экспонента по основанию 2 с оператором левого сдвига:
Аналогично,если b
является целым числом st 1 <= b <= 30
, вы можете использовать сдвиг влево (<<
) вместо первого Math.pow(2, b)
: 2 << (b - 1)
:
for (let i = 0; i <= 31; ++i) {
console.log(`Math.pow(2, ${ i }) === 2 << (${ i } - 1)? ${ Math.pow(2, i) === 2 << (i - 1) ? 'Yes' : 'No'}.` );
}
.as-console-wrapper {
max-height: 100vh !important;
}
➰ Используйте цикл while:
Заметили ли вы, что после применения метода запоминания мы не используем переменную i
внутрицикл больше?Теперь вы можете заменить for
на while
, что не даст большого прироста, но все же стоит упомянуть эту опцию:
while (--c) {
exp *= 1.2;
rv += ~~(a * exp);
}
? Окончательный результат:
В целом ваш сверхбыстрый код будет выглядеть так:
function original(a, b, c) {
rv = Math.floor(a * Math.pow(2, b));
for (i = 1; i < c; i++) {
rv += Math.floor(a * Math.pow(1.2, b+i));
}
return rv;
}
function faster(a, b, c) {
let rv = ~~(a * (2 << (b - 1)));
let exp = Math.pow(1.2, b);
while (--c) {
exp *= 1.2;
rv += ~~(a * exp);
}
return rv;
}
const expected = original(2, 2, 113);
const actual = faster(2, 2, 113);
const ok = expected === actual ;
if (ok) {
// BEFORE:
const t0b = performance.now();
for (let i = 0; i < 100000; ++i) {
original(2, 2, 113);
}
const tb = performance.now() - t0b;
// AFTER:
const t0a = performance.now();
for (let i = 0; i < 100000; ++i) {
faster(2, 2, 113);
}
const ta = performance.now() - t0a;
console.log(` BEFORE = ${ tb }s`);
console.log(` AFTER = ${ ta }s`);
console.log(`SPEEDUP = ${ Math.round(100 * tb / ta) / 100 } = ${ Math.round((1 - ta / tb) * 10000) / 100 }% faster!`);
} else {
console.log(`Faster version = ${ actual } does not return the same as before = ${ expected }`);
}
.as-console-wrapper {
max-height: 100vh !important;
}
un Развертывание цикла:
Стоит отметить, что, поскольку вы получаете результаты каждой операции, эта техника не будет ускорятьсяслишком много кода, так что, вероятно, оно того не стоит, учитывая, насколько многословным становится код.
Подробнее о развертывании цикла можно прочитать здесь .
Однако, есливы не представляли результаты, вы могли бы сохранить много вычислений:
function faster (a, b, c) {
let rv = a * (2 << (b - 1));
let exp = Math.pow(1.2, b);
const r = c % 4;
if (r === 0) {
exp *= 1.728;
rv += a * a * a * exp;
} else if (r === 1) {
c += 3;
} else if (r === 2) {
exp *= 1.2;
rv += a * exp;
c += 2;
} else if (r === 3) {
exp *= 1.44;
rv += a * a * exp;
c += 1;
}
a = Math.pow(a, 4);
c /= 4;
while (--c) {
exp *= 2.0736;
rv += a * exp;
}
return rv;
}
Как вы видите, преимущество в том, что вы сможете комбинировать вычисления нескольких итераций в одномодин, вместо того, чтобы просто дублировать их, как вы должны сделать в исходном коде.Только для демонстрационных целей:
function original(a, b, c) {
rv = Math.floor(a * Math.pow(2, b));
for (i = 1; i < c; i++) {
rv += Math.floor(a * Math.pow(1.2, b+i));
}
return rv;
}
function faster(a, b, c) {
let rv = ~~(a * (2 << (b - 1)));
let exp = Math.pow(1.2, b);
const r = c % 4;
if (r === 0) {
exp *= 1.2;
rv += ~~(a * exp);
exp *= 1.2;
rv += ~~(a * exp);
exp *= 1.2;
rv += ~~(a * exp);
} else if (r === 1) {
c += 3;
} else if (r === 2) {
exp *= 1.2;
rv += ~~(a * exp);
c += 2;
} else if (r === 3) {
exp *= 1.2;
rv += ~~(a * exp);
exp *= 1.2;
rv += ~~(a * exp);
c += 1;
}
c /= 4;
while (--c) {
exp *= 1.2;
rv += ~~(a * exp);
exp *= 1.2;
rv += ~~(a * exp);
exp *= 1.2;
rv += ~~(a * exp);
exp *= 1.2;
rv += ~~(a * exp);
}
return rv;
}
const expected = original(2, 2, 113);
const actual = faster(2, 2, 113);
const ok = expected === actual;
if (ok) {
// BEFORE:
const t0b = performance.now();
for (let i = 0; i < 100000; ++i) {
original(2, 2, 113);
}
const tb = performance.now() - t0b;
// AFTER:
const t0a = performance.now();
for (let i = 0; i < 100000; ++i) {
faster(2, 2, 113);
}
const ta = performance.now() - t0a;
console.log(` BEFORE = ${ tb }s`);
console.log(` AFTER = ${ ta }s`);
console.log(`SPEEDUP = ${ Math.round(100 * tb / ta) / 100 } = ${ Math.round((1 - ta / tb) * 10000) / 100 }% faster!`);
} else {
console.log(`Faster version = ${ actual } does not return the same as before = ${ expected }`);
}
.as-console-wrapper {
max-height: 100vh !important;
}
☝️ Совет:
Кроме того, вы должны прочитать https://stackoverflow.com/help/how-to-ask перед тем, как писать другой вопрос, чтобы люди не понизили его..