Skip to content

How can I make this code find amicable numbers in a determined range in javaScript?

I want to have a code that finds amicable numbers in a certain range, but it only outputs one amicable number instead of all amicable numbers in that range.

How can I solve it? I think it may be a scope error.

Amicable numbers are a pair of numbers that the sum of all the divisors of the first number equals the second number, and the sum of the divisors of the second number equals the first number.

Here is my code:

let sum1 = 1;
let sum2 = 1;

for (let num1 = 3; num1 < 1300; num1++) {
    for (let num2 = 3; num2 < 1300; num2++) {

        for (let div1 = 2; div1 < num1; div1++) {
            if (num1 % div1 === 0) {
                sum1 += div1
            }
        }

        for (let div2 = 2; div2 < num2; div2++) {
            if (num2 % div2 === 0) {
                sum2 += div2
            }
        }


        if (num1 === sum2 && sum1 === num2) {
            console.log(num1 + " and " + num2 + " are amicable numbers!")

        }
    }
}

Answer

Your code is very inefficient, having to calculate the sum of the divisors on every iterartion – even though you have previously calculated the sum of divisors for this number.

I tend to find it easier to think about this sort of problem in multiple steps. Step 1, calculate the sum of divisors for any given number:

const sumDivisors = num => {
  let res = 0;
  for(let i=1;i<num;i++)
    if( (num % i) == 0)
      res += i
  return res;
}

Step 2, gather the numbers in the desired range and all their sums of divisors

var divisors = new Array(1300).fill(0).map( (_,i) => i)
                              .reduce( (acc,num) => ({...acc, [num]: sumDivisors(num)}))

The above gives you an object with num as the key and sum (of divisors) as the value.

{
  "1": 0,
  "2": 1,
  "3": 1,
  "4": 3,
  "5": 1,
  "6": 6,
  "7": 1,
  "8": 7,
    .. etc
}

Step3, look for any item in the list where

  • key less than value (this also covers key != value and stops you getting both ways in the result ie 220,284 & 284,220)
  • value matches another key

Put it all together you get the below code which gives the expected results

const sumDivisors = num => {
  let res = 0;
  for(let i=1;i<num;i++)
    if( (num % i) == 0)
      res += i
  return res;
}

var divisors = new Array(1300).fill(0).map( (_,i) => i)
                              .reduce( (acc,num) => ({...acc, [num]: sumDivisors(num)}))
var amicable = Object.entries(divisors)
                     .filter( ([num,sum]) => num < sum && divisors[sum] == num);

for(let [num1,num2] of amicable)
  console.log(num1 + " and " + num2 + " are amicable numbers!")

You may like to compare the performance difference here between your original code (fixed to work) and the code in this answer: https://jsbench.me/jekosj89v4/1 The improvement in speed is a factor of 1000