算法找到最佳组合

时间:2009-03-25 07:15:30

标签: algorithm math

假设我有100个产品的清单,每个产品都有价格。每个还具有能量(kJ)测量值。

是否有可能找到15种产品的最佳组合,其中10美元以下的能量总和(kJ)最大,使用编程?

我知道C#,但任何语言都没问题。欢呼声。

更新:稍微有点发现背包问题的示例源代码。有没有人知道在哪里找到一些。谷歌搜索了几个小时,如果可能的话,需要在明天之前进行排序。 TA

10 个答案:

答案 0 :(得分:14)

http://en.wikipedia.org/wiki/Knapsack_problem

  

背包问题背包问题combinatorial optimization中的一个问题:给定一组项目,每个项目都有一个权重和一个值,确定数字每个项目包括在一个集合中,以使总重量小于或等于给定的限制,并且总值尽可能大。它的名字源于受固定大小knapsack约束的人所面临的问题,并且必须填写最有价值的物品......

答案 1 :(得分:6)

这听起来更像linear programming问题。

  

非正式地,线性编程   决定实现最佳的方式   结果(如最大利润或   给定数学中的最低成本)   模型和给出一些列表   要求表示为线性的   方程。

查看Simplex Method

答案 2 :(得分:4)

这在整数线性规划中,优化受线性约束的线性方程,其中所有变量和系数都是整数。

对于 i 的所有值,你想要变量includeItem1,...,includeItemN,约束0≤includeItem i ≤1,includeItem1 + ... +includeItemN≤ 15,includeItem1 * priceItem1 + ...≤10,最大化includeItem1 * kilojouleItem1 + ....

将其粘贴在您最喜欢的整数线性程序求解器中并获得解决方案:)

另见http://en.wikipedia.org/wiki/Linear_programming

说你的特定问题是NP完全没有意义,但它是NP完全(某种)问题的一个实例,所以理论上可能没有 快这样做的方式。根据您希望获得的最佳性和ILP求解器的工作速度有多接近,这在实践中可能是可行的。

我认为你的问题不是ILP的一个特例,它使得它特别容易解决。将它视为背包式问题,您可以限制自己查看1..100的所有子集,这些子集最多(或精确地)有15个元素,这是n的多项式 - 它是n-choose-15,它小于(n ^ 15)/(15!),但是当n = 100时,这并不是非常有用。

如果您需要求解器程序的建议,我尝试过glpk并发现它使用起来很愉快。如果你想要一些花钱的东西,我的讲师总是以CPLEX为例。

答案 3 :(得分:1)

这听起来很像背包问题。有各种方法(例如,按能量密度递减的顺序)。

答案 4 :(得分:1)

如果您可以选择产品,这是背包问题。如果您可以选择产品的小数值,那么您可以使用单纯形法解决这个问题,但分数背包问题有一个简单的解决方案。

按能源/价格比率订购商品,挑选最高价格的100%,直到您用完为止,然后选择剩余最高价格的小数值。

例如,如果价格是4,3,5,4,能量是3,5,2,7,则订购

7 / 4,5 / 3,3 / 4,2 / 5

所以你会选择4美元和2美元,这将花费7美元,剩下的3美元你将购买第一件商品的75%,价格为3美元,能量为3 * .75 = 2.25

这将使总能量为14.25

请注意,允许小数值会提供比仅允许0%或100%更高的目标值,因此没有整数解决方案会比14.25更好(或者因为目标值必须是整数,所以14或更好)

要解决原始的背包问题,你可以使用分支定界,这在实践中应该可以正常工作。假设您有一个目标值为z *

的当前候选解决方案
  1. 解决轻松的问题,即允许分数权重。如果该值小于z *,则丢弃此分支。
  2. 计算新值z,这是在没有最后一个分数权重的情况下找到的解决方案,如果此值大于z *,则用新值替换z *
  3. 选择一项(比如列表中的第一项,最赚钱的项目)并形成两个子问题,一个是必须将其包含在解决方案中,另一个不能将其包含在解决方案中(这是分支步骤)。
  4. 只要您有解决的子问题,请选择一个并返回步骤1.
  5. 请注意,当您创建必须选择项目的子问题时,只需从预算中减去其价格并将该值添加到利润中,现在您需要解决较小的问题。

    有关更详细的说明,请查看维基百科上的Branch and Bound

答案 5 :(得分:1)

Stony Brook算法存储库列出implementations for the knapsack problem.

他们的书The Algorithm Design Manual有大量问题的这类信息。

答案 6 :(得分:1)

是的,正如每个人都指出这是一个复杂的背包问题。这个简单的东西虽然可能很好......

SELECT TOP 15 *
FROM Product
WHERE Price < 10
ORDER BY Energy DESC

答案 7 :(得分:0)

这让我想起了着名的背包算法

  

http://en.wikipedia.org/wiki/Knapsack_problem

答案 8 :(得分:0)

应该可以用Cream for Java来解决问题。还有一个适用于C#的版本CSharpCream

答案 9 :(得分:0)

在个人项目中做了类似的事情,但是用php代码。 如果您想移植到C#,请放心。

此代码考虑了多个组合相同的可能性,因此返回值将是x个最佳结果的数组。

注意:这是考虑到任何一项在每个结果中可以使用0或1次

<?php
$products = [
    ['id' => 1, 'price' => 3.00, 'energy' => 200],
    ['id' => 2, 'price' => 14.10, 'energy' => 3200],
    ['id' => 3, 'price' => 2.66, 'energy' => 300],
    ['id' => 4, 'price' => 5.00, 'energy' => 450],
    ['id' => 5, 'price' => 6.23, 'energy' => 667],
    ['id' => 6, 'price' => 7.00, 'energy' => 1200]
];

function genCombinations($values, $count = 0)
{

    // Figure out how many combinations are possible:

    $comboCount = pow(count($values) , $count);
    $r = [];

    // Iterate and add to array

    for ($i = 0; $i < $comboCount; $i++){
        $r[] = getCombination($values, $count, $i);
    }
    return $r;
}


// State-based way of generating combinations:

function getCombination($values, $count, $index)
{
    $result = [];
    for ($i = 0; $i < $count; $i++) {

        // Figure out where in the array to start from, given the external state and the internal loop state

        $pos = $index % count($values);

        // Append and continue

        $result[] = $values[$pos];
        $index = ($index - $pos) / count($values);
    }
    return $result;
}

//maximize energy for given price

function getBestProductCombinations($products,$price_limit){

    //find all combinations where each product is either selected or not - true or false

    $combos = genCombinations([true,false],count($products));

    $results = [];
    foreach($combos as $combo){

        //loop through each combination and get a result

        $sum_price = 0;$items = [];$sum_energy = 0;
        foreach($combo as $i => $o){

            //loop through the array of true/false values determining if an item is on or off

            if($o){

                //if on, add item to result

                $sum_price += $products[$i]['price'];
                $sum_energy += $products[$i]['energy'];
                $items[] = $products[$i];
            }
        }
        if($sum_price <= $price_limit){

            //if sum of result is within the price limit, add to the results array

            $results[] = [
                'items' => $items,
                'price' => $sum_price,
                'energy' => $sum_energy
            ];
        }
    }

    $best = $results[0];$ra = [$best];
    foreach($results as $k => $result){
        if($k === 0){continue;}//skip first iteration as it was set above

        //check if the energy is higher than the best, or if equal, check if the price is better

        if($result['energy'] > $best['energy'] || ($result['energy'] === $best['energy'] && $result['price'] < $best['price'])){

            //reset best to the current result, reset return array

            $best = $result;
            $ra = [$best];
        }else if($result['energy'] === $best['energy']){

            //current result is the same as best, add it to the return array

            $ra[] = $result;
        }
    }
    return $ra;
}

echo '<pre>'.json_encode(getBestProductCombinations($products,10),JSON_PRETTY_PRINT).'</pre>';

那会给你的:

[
    {
        "items": [
            {
                "id": 3,
                "price": 2.66,
                "energy": 300
            },
            {
                "id": 6,
                "price": 7,
                "energy": 1200
            }
        ],
        "price": 9.66,
        "energy": 1500
    }
]