【问题标题】:Find the lowest combination of predefined numbers, which sum is higher than X找到预定义数字的最低组合,其总和高于 X
【发布时间】:2016-12-18 15:52:42
【问题描述】:

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

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€

}

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

我对 JS 很陌生,我正在考虑计算我的项目的每一个组合,然后使用与购买价格差异最小的那个。这对我来说似乎真的很复杂,我什至不知道如何让它计算每个组合并保存用于计算的项目。

有什么方法可以更有效地实现这一点吗?我真的不指望这里有任何完美的工作代码,对进入正确方向的一点帮助也会很好。

感谢任何帮助! :)


编辑:

很抱歉错过了我自己的尝试。只是我根本不知道应该如何解决这个问题。不 - 不是家庭作业 - 这应该是我正在研究的 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  

【问题讨论】:

  • 看起来很像家庭作业。请添加您尝试过的代码。
  • 这不是代码编写服务。您至少需要展示您的代码尝试或为解决此问题所做的研究,当您提供的代码无法按预期工作时,人们会提供帮助。见How to AskQuestion checklist
  • 请见谅。我不希望人们编写我的代码(“...一点点帮助以进入正确的方向也很好!”)。我没有添加自己的方法,因为我真的不知道从哪里开始 - 不知道如何解决这个问题。我自己的尝试只是将数组中的数字相加,直到值高于预期数字,然后返回一个包含位置的数组。我将它添加到我的原始帖子中。很抱歉对于这个误会! @charlietfl
  • 如果你想要最多数量的项目可以对数组进行排序并开始求和直到超过目标然后停止
  • @charlietfl - 对数组进行排序并不是一个好的解决方案。我在原始帖子中添加了一个示例。如果我对数组进行排序并开始将数字相加,我将得到 1.34(如果我想要的数字是 1.18)。这是 0.16 的差异。如果我只是从数组中添加正确的两个数字,我可以得到 0.01 的差异,这会更好。

标签: javascript jquery math calculation


【解决方案1】:

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

基本考虑是取一个从零到 2values.length 的计数器,并检查实际的 2index 是否是带有 bitwise AND &amp; 的计数器的一部分.如果是,则从索引中取出值并将其放入parts 数组中。

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

此建议适用于未排序的值,但如果大于 target 值的值不包含在要处理的数组中,则效率可能更高。

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

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);
.as-console-wrapper { max-height: 100% !important; top: 0; }

【讨论】:

    【解决方案2】:

    我建议采取一些措施:

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

    我在这里提出的解决方案将在很明显继续添加项目没有意义后立即回退。至少有两种情况可以得出这个结论(停止递归搜索):

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

    这是建议的代码:

    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));

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-03-11
      • 1970-01-01
      • 2022-01-05
      • 1970-01-01
      • 2016-04-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多