Optimisation de la vitesse au détriment de la mémoire en JavaScript
Il existe des situations où il est possible de sacrifier de la mémoire vive pour améliorer les performances.
Regardons un exemple. Le code suivant trouve les nombres amicaux dans un intervalle donné :
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;
}
Le code ci-dessus n'est pas optimal.
Il effectue un grand nombre d'opérations
et avec l'intervalle allant jusqu'à 9000
la page du navigateur va simplement geler.
Le problème de ce code est que nous
calculons la somme des diviseurs de chaque
nombre un très grand nombre de fois, autant
que de nombres vérifiés au total.
Cela signifie que dans notre cas
pour n'importe quel nombre, la somme de ses diviseurs
sera trouvée 9000 fois.
Il n'est pas surprenant que tout se bloque.
Optimisons. Pour commencer, créons une fonction calculant directement la somme des diviseurs, sans les sauvegarder dans un tableau :
function getOwnDivisorsSum(num) {
let sum = 0;
for (let i = 1; i < num; i++) {
if (num % i === 0) {
sum += i;
}
}
return sum;
}
Maintenant, il est temps de sacrifier de la mémoire vive. Créons une fonction qui calculera une fois pour toutes la somme des diviseurs de tous les nombres de l'intervalle donné et les sauvegardera dans un tableau.
Notre fonction retournera un tableau, dans lequel la clé sera le nombre (moins un), et la valeur sera la somme de ses diviseurs. Implémentons notre fonction :
function getAllSum(range) {
let arr = [];
for (let i = 1; i <= range; i++) {
arr.push(getOwnDivisorsSum(i));
}
return arr;
}
Maintenant, pour vérifier l'amicalité, nous ne calculerons pas à chaque fois la somme des diviseurs des nombres, nous prendrons simplement celle déjà calculée dans le tableau :
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;
}
Rassemblons tout et obtenons le code suivant :
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;
}
Le code suivant trouve les nombres premiers entre eux dans un intervalle donné. Optimisez-le :
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;
}