找到预定义数字的最低组合,其总和高于X.

时间:2016-12-18 15:52:43

标签: javascript jquery math calculation

假设我有一个具有不同商品价格的数组。

var myItemsEuro = [0.34, 0.11, 0.5, 0.33, 0.05, 0.13, 0.23, 3.22, 1.94]

我想有这样的功能:

function getTradeItems(0.89) {   //The price of the item I want to buy

    //Calculate, which of my items should be used to buy the item for 0.89€

    return [0, 3, 6]    //The position of my items in the array, which added together equal 0.90€

}

清理事项:
我有一盒带有pricetags的物品(myItemsEuro)。我想买一件物品,用我的物品作为付款。如果我用至少1美分多付钱,另一方将接受我的交易。 该功能应该有效,所以我可以将其他人的价格传递给它(例如0.89)并返回,我将不得不放弃哪些物品。这些项目的组合必须高于0.89美分(至少0.9),但应尽可能低!

我是JS的新手,我正在考虑计算我项目的每一个组合,然后使用与buyprice差异最小的项目。这对我来说似乎很复杂,我甚至不知道如何计算每一个组合,还保存用于计算的项目。

有没有办法更有效地实现这一目标?我真的不希望这里有任何完美的代码,一点点帮助才能进入正确的方向也很不错。

任何帮助表示赞赏! :)

修改:

很抱歉错过了我自己的尝试。只是我根本不知道如何解决这个问题。不 - 不是作业 - 这应该是我正在努力的chromeextension的一部分!

var myItemsEuro = [0.34, 0.11, 0.5, 0.33, 0.05, 0.13, 0.23, 3.22, 1.94]

function getTradeItems(marketPrice) {

	var result = 0;

	var positions = [];

	for(i = 0; i < myItemsEuro.length; i++) {

		result += myItemsEuro[i]; //add numbers from the array

		positions.push(i); //save the used numbers position

		if(result > marketPrice) { //if result is greater than marketPrice...

			console.log(result)
            console.log(positions)

			return positions; //return positions in the array

		}
	
	}
}

getTradeItems(1.31);

修改

对数组进行排序然后添加数字并不能提供解决方案。

var x = 1.18;

   //Sorted by numbers
var myItemsEuro = [0.05, 0.11, 0.13, 0.20, 0.35, 0.50, 0.60, 0.69, 0.75];

   //Add together and stop when sum > x:
0.05 + 0.11 + 0.13 + 0.20 + 0.35 + 0.50 = 1.34

   //Best solution would be adding [6] and [8] from the array
0.50 + 0.69 = 1.19  

2 个答案:

答案 0 :(得分:0)

如果项目大于或等于target,您可以使用暴力方法测试项目的所有组合。

基本考虑是将计数器从零到2 values.length 并检查实际的2 index 是否是具有{{3的计数器的一部分}}。如果是这样,请从索引中获取值并将其放入parts数组。

然后构建sum的{​​{1}}并检查parts是否大于或等于sum且可能小于target sum } array,然后将结果移动到结果数组。如果result等于sum,则将实际的result[0].sumparts推送到sum

此提案适用于未排序的值,但如果大于result值的值未包含在要处理的数组中,则可能更有效。

另一个缺点是,bitwise AND &仅适用于32位,这意味着不能使用超过32项的数组。

&#13;
&#13;
target
&#13;
var values = [0.34, 0.11, 0.5, 0.33, 0.05, 0.13, 0.23, 3.22, 1.94],
    target = 0.90,
    i,
    l = 1 << values.length,
    result = [],
    parts,
    sum;

for (i = 0; i < l; i++) {
    parts = values.filter(function (_, j) {
        return i & 1 << j;
    });
    sum = parts.reduce(function (a, b) { return a + b; }, 0);
    if (sum >= target) {
        if (!result.length || sum < result[0].sum) {
            result = [{ parts: parts, sum: sum }];
            continue;
        }
        if (sum === result[0].sum) {
            result.push({ parts: parts, sum: sum });
        }
    }
}

console.log(result);
&#13;
&#13;
&#13;

答案 1 :(得分:0)

我建议采取一些措施:

  • 小数可能会导致浮点精度问题,因此最好先将每个值转换为整数(即​​以美分为单位)
  • 执行递归搜索,在每个递归级别,您将决定是否在相应的索引处获取该项目。
  • 考虑一下您可能有多个解决方案的情况:在这些情况下,您可能希望优先考虑使用较少项目的解决方案。因此,您需要跟踪所选项目的数量。

我建议的解决方案一旦明确继续添加项目就没有任何意义,将立即回溯。至少有两种情况可以得出这个结论(停止递归搜索):

  • 当目前为止选择的值之和已经大于目标值时,添加更多项目没有意义(通过递归)
  • 如果在位置i处添加项目后,很明显添加所有剩余项目(通过递归)会导致总和低于目标,那么不选择位置i处的项目是没有意义的,并重复递归搜索,因为它肯定不会达到目标值。

以下是建议的代码:

&#13;
&#13;
function getTradeItems(target, items) {
    var best = -1e20, // should be maximised up to -1 if possible
        bestTaken = [], // indices of the best selection
        bestCount = 0, // number of selected items in best solution
        // Multiply all amounts by 100 to avoid floating point inaccuracies:
        cents = items.map( euro => Math.round(euro * 100) );
    function recurse(target, i, count) {
        if (target < 0) { // Found potential solution
            // Backtrack if this is not better than the best solution
            // found so far. If a tie, then minimise the number of selected items
            if (target < best || 
                target === best && count > bestCount) return false;
            best = target;
            bestTaken = [];
            bestCount = count;
            return true;
        }
        // Give up when there's not enough item value available
        if (i >= cents.length) return null;
        // Include the item at index i:
        var res1 = recurse(target - cents[i], i+1, count+1);
        // If we could not reach the target, don't lose time retrying
        // without this item:
        if (res1 === null) return null;
        // Exclude the item at index i:
        var res0 = recurse(target, i+1, count);
        // If neither led to a solution...
        if (!res0 && !res1) return false;
        // If the best was to include the item, remember that item
        if (!res0) bestTaken.push(i);
        return true;
    }
    recurse(Math.round(target * 100), 0);
    return bestTaken.reverse();
}

// Sample input
var myItemsEuro = [0.05, 0.11, 0.13, 0.20, 0.35, 0.50, 0.60, 0.69, 0.75];
var x = 1.18
// Get the best item selection
var result = getTradeItems(x, myItemsEuro);
// Output result
console.log('Selected: ', result.join()); // 5, 7
// Show corresponding sum: (1.19)
console.log('Sum: ', result.reduce( (sum, i) => sum + myItemsEuro[i], 0).toFixed(2));
&#13;
&#13;
&#13;