1

My problem is I have a number for example 17; I also have three other constant numbers: 2, 5, 7;

I need to find all possible combinations which make the specific number 17 or any other number;

5 + 5 + 7 = 17 (1 combination)
5 + 5 + 5 + 2 = 17 (2 combinations)
2 + 2 + 2 + 2 + 2 + 7 = 17 (3 combinations)
2 + 2 + 2 + 2 + 2 + 2 + 5 = 17 (4 combinations)

So the answer is 4. I created my script which working correctly with the number 17 but wrong with bigger numbers as 20 or 30. How to make it working with all numbers ?

const seek = 30;
const firstNum = 2;
const secondNum = 5;
const thirdNum = 7;
let combinations = 0;
const maxDivisor = Math.round(seek / 2);

for (let i = 1; i <= maxDivisor; i += 1) {
    if (secondNum * i + thirdNum === seek) {
        combinations++
    } else if (secondNum * i + firstNum === seek) {
        combinations++
    } else if (firstNum * i + secondNum === seek) {
        combinations++
    } else if (firstNum * i + thirdNum === seek) {
        combinations++
    } else if (thirdNum * i + firstNum === seek || thirdNum * i + secondNum === 0) {
        combinations++
    } else if (firstNum + secondNum + thirdNum === seek) {
        combinations++
    } else if (firstNum * i === seek || thirdNum * i === seek || secondNum * i === seek) {
        combinations++
    }
}
console.log(combinations);
James
  • 20,957
  • 5
  • 26
  • 41
Simba
  • 303
  • 1
  • 4
  • 17
  • In your second combination you first have a 5 and then a 2, and in the third you first have a 2 and then a 7. So do you want to count: `2 + 5 = 7` and `5 + 2 = 7` as two combinations? – The Coding Wombat Oct 20 '18 at 12:35
  • This is a arithmetic modular problem, called Linear Diophantine Equation. I suggest you to see [this question](https://stackoverflow.com/questions/5513129/solving-a-linear-diophantine-equationsee-description-for-examples) – javrd Oct 20 '18 at 12:53

4 Answers4

1

Simple solution is to first calculate max multipliers for each number and then keep summing all the possible combinations.

/** LOGIC **/

function getCombinations(inputNumber, pieceNumbers) {
  const combinations = []
  const initial = maxes(inputNumber, pieceNumbers);
  let divs = initial;
  const sum = createSum(pieceNumbers);
  while (!allZeros(divs)) {
    if (sum(divs) === inputNumber) {
      combinations.push(divs);
    }
    divs = decrement(divs, initial);
  }
  return combinations;
}

/**
 * returns max multiplier for each number
 * that is less than input number
 * ie. for [2, 5] and input 17
 * you get [8 (17 / 2); 3 (17 / 5)]
 */
function maxes(inputNumber, pieceNumbers) {
  return pieceNumbers.map((num, i) =>
    inputNumber / num | 0
  )
}

/**
 * decrements list of numbers till it contains only zeros
 * if we have divs [2, 0] and initial [2, 5] the result
 * will be [1, 5]
 */
function decrement(divs, initial) {
  const arr = divs.slice();
  let i = arr.length;
  while (i--) {
    if (arr[i] > 0) {
      return [...arr.slice(0, i), arr[i] - 1, ...initial.slice(i + 1)];
    }
  }
}

function allZeros(divs) {
  return divs.every(div => div === 0);
} 

function createSum(pieceNumbers) {
  return (divs) => divs.reduce((acc, itm, i) => acc + itm * pieceNumbers[i], 0);
}

function toPrint(combinations, pieceNumbers) {
  const printable = combinations.map(nums =>
    nums.map(
      (n, i) => Array(n).fill(pieceNumbers[i]).join(" + ")
    )
      .filter(x => x)
      .join(" + ")
  ).join("\n");
  return printable;
}

/** VIEW **/

const addPieceEl = document.querySelector(".js-add-piece-number");
const removePieceEl = document.querySelector(".js-remove-piece-number");
const calculateEl  = document.querySelector(".js-calculate");
const displayEl = document.querySelector(".js-display-result");

addPieceEl.addEventListener("click", () => {
  addPieceEl.insertAdjacentHTML("beforebegin", ` <input type="number" class="js-piece-number number" value="7" /> `)
})

removePieceEl.addEventListener("click", () => {
  addPieceEl.previousElementSibling.remove()
})

calculateEl.addEventListener("click", () => {
  const inputNumber = Number(document.querySelector(".js-input-number").value);
  const pieceNumbers = Array.from(document.querySelectorAll(".js-piece-number")).map(el => Number(el.value))
  const combinations = getCombinations(inputNumber, pieceNumbers);
  const total = `There are ${combinations.length} combinations for ${inputNumber} with ${pieceNumbers.join(", ")}:\n`;
  displayEl.textContent = total + toPrint(combinations, pieceNumbers);
});
.number {
width: 30px;
}
Input Number: <input type="number" class="js-input-number number" value="17"/>
<br/>
<br/>
Piece Numbers:
<input type="number" class="js-piece-number number" value="2"/>
<input type="number" class="js-piece-number number" value="5"/>
<input type="number" class="js-piece-number number" value="7"/>
<button class="js-add-piece-number">+</button>
<button class="js-remove-piece-number">-</button>
<br/>
<br/>
<button class="js-calculate">calculate</button>
<pre class="js-display-result"/>
marzelin
  • 10,790
  • 2
  • 30
  • 49
0

You can test all combinations

const n = 30;
console.log('Number to reach: n = ' + n);

const k = [7, 5, 2];
console.log('Numbers to use: k = ' + k);

console.log('n can be wrote: n = a*k[0] + b*k[1] + c*k[2]');
console.log('Let\'s found all combinations of [a, b, c]');
let t = [];
for (let a = 0; n >= a*k[0]; a++)
  for (let b = 0; n >= a*k[0] + b*k[1]; b++)
    for (let c = 0; n >= a*k[0] + b*k[1] + c*k[2]; c++)
      if (n == a*k[0] + b*k[1] + c*k[2])
        t.push([a, b, c]);

console.log('Number of combinations: ' + t.length);
for (let i = 0; i < t.length; i++)
  console.log(
    n + ' = ' + new Array()
    .concat(new Array(t[i][0]).fill(k[0]).join(' + '))
    .concat(new Array(t[i][1]).fill(k[1]).join(' + '))
    .concat(new Array(t[i][2]).fill(k[2]).join(' + '))
    .filter(e => e.length > 0)
    .join(' + ')
  );
alex
  • 5,661
  • 6
  • 33
  • 54
0

I am assuming you only want solutions that are unique in the sense that 2+5 and 5+2 aren't unique.

Using recursion you can create an algorithm that can solve this problem for every number and every list of constants.

    const seek = 17;
    const numbs = [2,5,7];
    const minNum = Math.min(...numbs);
    let numberOfCombinations = 0;

    seekCombinations(seek, numbs);

    function seekCombinations (toSeek, numbs) {
        for (let i = 0; i < numbs.length; i++) {
            let newToSeek = toSeek - numbs[i];

            if (newToSeek === 0) { //you found a combination

                numberOfCombinations++;

            } else if (newToSeek >= minNum) { //The new number to seek can still form a combination

                //remove numbers from your list of constants that are smaller then then the number that is already in your current combination so you'll only get unique solutions.
                let index = numbs.indexOf(numbs[i]);
                let newNumbs = numbs.slice(index, numbs.length);

                //recursively call seekCombinations
                seekCombinations (newToSeek, newNumbs, currentCombination)
            }
        }
    }
The Coding Wombat
  • 805
  • 1
  • 10
  • 29
-1

so whats the question? whats wrong with your script or how to do it?

in case you missed it,

multiplying the number with i doesnt solve this problem. because:

30 = 2 + 2 + 5 + 7 + 7 + 7
30 = 2 + 2 + 2 + 5 + 5 + 7 + 7

any number of one of those 3 constants can appear there.

these cases are not covered by your script

Nikko Khresna
  • 1,024
  • 8
  • 10