Optimalizácia rýchlosti na úkor pamäte v JavaScripte
Existujú situácie, kedy je možné obetovať operačnú pamäť pre zvýšenie rýchlosti.
Pozrime sa na príklad. Nasledujúci kód hľadá priateľské čísla v zadanom rozsahu:
console.log(getFriendly(9000));
function getFriendly(range) {
let res = [];
for (let i = 1; i <= range; i++) {
for (let j = 1; j < range; j++) {
if (isFriendly(i, j)) {
res.push([i, j]);
}
}
}
return res;
}
function isFriendly(num1, num2) {
let sum1 = getSum(getOwnDivisors(num1));
let sum2 = getSum(getOwnDivisors(num2));
return sum1 == num2 && sum2 == num1;
}
function getOwnDivisors(num) {
let res = [];
for (let i = 1; i < num; i++) {
if (num % i === 0) {
res.push(i);
}
}
return res;
}
function getSum(arr) {
let sum = 0;
for (let elem of arr) {
sum += elem;
}
return sum;
}
Uvedený kód nie je optimálny.
Vykonáva veľké množstvo operácií
a pri zadanom rozsahu do 9000
stránka prehliadača jednoducho zamrzne.
Problém tohto kódu je v tom, že
pre každé číslo počítame súčet jeho
deliteľov veľmi veľakrát, toľkokrát,
koľko čísiel celkovo kontrolujeme.
To znamená, že v našom prípade
pre akékoľvek číslo bude súčet jeho deliteľov
nájdený 9000 krát.
Nečudo, že všetko zamrzá.
Poďme optimalizovať. Na začiatok urobme funkciu, ktorá priamo vypočíta súčet deliteľov, bez ukladania ich do poľa:
function getOwnDivisorsSum(num) {
let sum = 0;
for (let i = 1; i < num; i++) {
if (num % i === 0) {
sum += i;
}
}
return sum;
}
Teraz prišiel čas obetovať operačnú pamäť. Urobme funkciu, ktorá vopred raz vypočíta súčet deliteľov všetkých čísiel z zadaného rozsahu a uloží ich do poľa.
Výsledkom našej funkcie bude pole, v ktorom kľúčom bude číslo (o jedna menšie), a hodnotou - súčet jeho deliteľov. Realizujme našu funkciu:
function getAllSum(range) {
let arr = [];
for (let i = 1; i <= range; i++) {
arr.push(getOwnDivisorsSum(i));
}
return arr;
}
Teraz pre kontrolu priateľskosti nebudeme zakaždým počítať súčet deliteľov čísiel, ale jednoducho budeme brať už vypočítaný z poľa:
function getFriendly(range) {
let sums = getAllSum(range); // [1, 2, 6...]
let res = [];
for (let i = 0; i < sums.length; i++) {
for (let j = i; j < sums.length; j++) {
let sum1 = sums[i];
let sum2 = sums[j];
let num1 = i + 1;
let num2 = j + 1;
if (num1 == sum2 && num2 == sum1) {
res.push([num1, num2]);
}
}
}
return res;
}
Poďme všetko spolu a získame nasledujúci kód:
console.log(getFriendly(9000));
function getFriendly(range) {
let sums = getAllSum(range);
let res = [];
for (let i = 0; i < sums.length; i++) {
for (let j = i; j < sums.length; j++) {
let sum1 = sums[i];
let sum2 = sums[j];
let num1 = i + 1;
let num2 = j + 1;
if (num1 == sum2 && num2 == sum1) {
res.push([num1, num2]);
}
}
}
return res;
}
function getAllSum(range) {
let arr = [];
for (let i = 1; i <= range; i++) {
arr.push(getOwnDivisorsSum(i));
}
return arr;
}
function getOwnDivisorsSum(num) {
let sum = 0;
for (let i = 1; i < num; i++) {
if (num % i === 0) {
sum += i;
}
}
return sum;
}
Nasledujúci kód hľadá nesúdeliteľné čísla z zadaného rozsahu. Optimalizujte ho:
console.log(getRelativelyPrime(10000));
function getRelativelyPrime(range) {
let res = [];
for (let i = 2; i <= range; i++) {
for (let j = 2; j < range; j++) {
if (isRelativelyPrime(i, j)) {
res.push([i, j]);
}
}
}
return res;
}
function isRelativelyPrime(num1, num2) {
let arr1 = getDivisors(num1);
let arr2 = getDivisors(num2);
let int = getIntersect(arr1, arr2);
if (int.length === 0) {
return true;
} else {
return false;
}
}
function getIntersect(arr1, arr2) {
let result = [];
for (let elem of arr1) {
if (arr2.includes(elem)) {
result.push(elem);
}
}
return result;
}
function getDivisors(num) {
let res = [];
for (let i = 2; i <= num; i++) {
if (num % i === 0) {
res.push(i);
}
}
return res;
}