简体   繁体   中英

Finding closest sum of numbers to a given number

Say I have a list [1,2,3,4,5,6,7] and I would like to find the closest sum of numbers to a given number. Sorry for the crappy explanation but here's an example:

Say I have a list [1,2,3,4,5,6,7] I want to find the closest numbers to, say, 10.

Then the method should return 6 and 4 or 7 and 3 because its the closest he can get to 10. So 5 + 4 would be wrong because thats 9 and he can make a 10.

another example : you want the closest to 14 , so then he should return 7 and 6

If you got any questions plz ask because its difficult to explain what I want :P

 var data= [1, 2, 3,4,5,6,7];
  var closest = 14;
 for (var x = 0; x < data.length; x++) {
    for (var y = x+1; y < data.length; y++) {
             if(data[x] + data[y] == closet){
                     alert(data[x].toString() + "  " + data[y].toString());
                }
            }
       }

From what I understood from your question, I made this snippet. I assumed you did not wanted to have the same digit twice (eg 14 => 7 + 7).

It is working with your examples.

 var arr = [1, 2, 3, 4, 5, 6, 7]; var a = 0, b = 0; var nb = 14; for(var i in arr) { for(var j in arr) { if(i != j) { var tmp = arr[i] + arr[j]; if(tmp <= nb && tmp > a + b) { a = arr[i]; b = arr[j]; } } } } document.write("Closest to " + nb + " => " + a + " + " + b);

I have a little bit long winded solution to the problem just so it is easier to see what is done.

The main benefits with solution below:

  • The second loop will not start from beginning of the array again. What I mean that instead of having loop_boundary for second loop as 0 as you normally would, here it starts from next index. This helps if your numbers array is long. However, if it as short as in example, the impact in performance is minimal. Decreasing first loop's boundary by one will prevent errors from happening.
  • Works even when the wanted number is 1 or negative numbers.

Fiddle:

JSFiddle

The code:

var numbers = [1,2,3,4,5,6,7];
var wanted_number = 1;
var closest_range, closest1, closest2 = null;

var loop1_boundary = numbers.length-1;
for(var i=0; i<loop1_boundary; i++) {

    var start_index = i+1;
    var loop2_boundary = numbers.length;

    for(var k=start_index; k<loop2_boundary; k++) {

        var number1 = parseInt(numbers[i]);
        var number2 = parseInt(numbers[k]);

        var sum = number1 + number2;
        var range = wanted_number - sum;

        document.write( number1+' + '+number2 +' < '+closest_range+'<br/>' );

        if(Math.abs(range) < Math.abs(closest_range) || closest_range == null ) {
            closest_range = range;
            closest1 = number1;
            closest2 = number2;
        }

    }

    if(range==0){
        break;
    }
}

document.write( 'closest to given number was '+closest1+' and '+closest2+'. The range from wanted number is '+closest_range );

This proposal generates all possible combinations , collects them in an object which takes the sum as key and filters then the closest sum to the given value.

 function getSum(array, sum) { function add(a, b) { return a + b; } function c(left, right) { var s = right.reduce(add, 0); if (s > sum) { return; } if (!result.length || s === result[0].reduce(add, 0)) { result.push(right); } else if (s > result[0].reduce(add, 0)) { result = [right]; } left.forEach(function (a, i) { var x = left.slice(); x.splice(i); c(left.slice(0, i), [a].concat(right)); }); } var result = []; c(array, [], 0); return result; } function print(o) { document.write('<pre>' + JSON.stringify(o, 0, 4) + '</pre>'); } print(getSum([1, 2, 3, 4, 5, 6, 7], 10)); print(getSum([1, 2, 3, 4, 5, 6, 7], 14)); print(getSum([1, 2, 3, 4, 5, 6, 7], 19));

Functions for combine , locationOf , are taken from different answers, written by different authors.

 printClosest([0.5,2,4] , 5); printClosest([1, 2, 3, 4, 5, 6, 7], 28); printClosest([1, 2, 3, 4, 5, 6, 7], 10.9); printClosest([1, 2, 3, 4, 5, 6, 7], 10, 2); printClosest([1, 2, 3, 4, 5, 6, 7], 10, 3); printClosest([1, 2, 3, 4, 5, 6, 7], 14, 2); function printClosest(array, value, limit) { var checkLength = function(array) { return array.length === limit; }; var combinations = combine(array); //get all combinations combinations = limit ? combinations.filter(checkLength) : combinations;//limit length if required var sum = combinations.map(function(c) { //create an array with sum of combinations return c.reduce(function(p, c) { return p + c; }, 0) }); var sumSorted = sum.slice(0).sort(function(a, b) {//sort sum array return a - b; }); index = locationOf(value, sumSorted);//find where the value fits in //index = (Math.abs(value - sum[index]) <= Math.abs(value - sum[index + 1])) ? index : index + 1; index = index >= sum.length ? sum.length - 1 : index; index = sum.indexOf(sumSorted[index]);//get the respective combination console.log(sum, combinations, index); document.getElementById("result").innerHTML += "value : " + value + " combi: " + combinations[index].toString() + " (limit : " + (limit || "none") + ")<br>"; } function combine(a) { var fn = function(n, src, got, all) { if (n == 0) { if (got.length > 0) { all[all.length] = got; } return; } for (var j = 0; j < src.length; j++) { fn(n - 1, src.slice(j + 1), got.concat([src[j]]), all); } return; } var all = []; for (var i = 0; i < a.length; i++) { fn(i, a, [], all); } all.push(a); return all; } function locationOf(element, array, start, end) { start = start || 0; end = end || array.length; var pivot = parseInt(start + (end - start) / 2, 10); if (end - start <= 1 || array[pivot] === element) return pivot; if (array[pivot] < element) { return locationOf(element, array, pivot, end); } else { return locationOf(element, array, start, pivot); } }
 <pre id="result"><pre>

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM