Optimering av hastighet på bekostnad av minne i JavaScript
Det finns situationer där man kan offra arbetsminne för att öka prestandan.
Låt oss titta på ett exempel. Följande kod hittar vänskapliga tal inom ett givet intervall:
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;
}
Koden ovan är inte optimal.
Den gör ett stort antal operationer
och med det angivna intervallet upp till 9000
kommer webbläsarsidan att hänga sig.
Problemet med denna kod är att vi
för varje tal beräknar summan av dess
delare väldigt många gånger, lika många
som det totala antalet tal vi kontrollerar.
Det betyder att i vårt fall
kommer summan av delarna för vilket tal som helst
att hittas 9000 gånger.
Inte konstigt att allting hänger sig.
Låt oss optimera. Till att börja med skapar vi en funktion som direkt beräknar summan av delare, utan att spara dem i en array:
function getOwnDivisorsSum(num) {
let sum = 0;
for (let i = 1; i < num; i++) {
if (num % i === 0) {
sum += i;
}
}
return sum;
}
Nu är det dags att offra arbetsminne. Låt oss skapa en funktion som i förväg en gång beräknar summan av delare för alla tal från det givna intervallet och sparar dem i en array.
Vår funktion kommer att returnera en array, där nyckeln kommer att vara talet (minus ett), och värdet blir summan av dess delare. Låt oss implementera vår funktion:
function getAllSum(range) {
let arr = [];
for (let i = 1; i <= range; i++) {
arr.push(getOwnDivisorsSum(i));
}
return arr;
}
Nu för att kontrollera vänskaplighet kommer vi inte varje gång beräkna summan av talens delare, utan vi kommer helt enkelt ta den redan beräknade från arrayen:
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;
}
Låt oss samla allt och få följande kod:
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;
}
Följande kod hittar relativt prima tal från ett givet intervall. Optimera den:
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;
}