【问题标题】:algorithm to find best combination寻找最佳组合的算法
【发布时间】:2009-03-25 07:15:29
【问题描述】:

假设我有一个包含 100 种产品的列表,每种产品都有一个价格。每一个也有一个能量 (kJ) 测量值。

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

我知道 C#,但任何语言都可以。干杯。

更新: 为背包问题找到一些示例源代码有点麻烦。有没有人有或知道在哪里可以找到一些。已经在谷歌上搜索了几个小时,如果可能的话,需要在明天之前对其进行排序。塔。

【问题讨论】:

  • 在这个问题中,您将看到一组 n 个奖品,您可以从中选择最多 m 个奖品,其中 m

标签: algorithm math


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

    这听起来更像是linear programming 问题。

    非正式地,线性规划 确定实现最佳的方法 结果(例如最大利润或 最低成本)在给定的数学 模型并给出一些列表 需求表示为线性 方程。

    查看Simplex Method

    【讨论】:

    • +1 现货。查看 lp_Solve。它是开源的,可以满足您的需求,并提供包括 C# 在内的多种语言的示例。
    • 这不只是给我这 15 种产品的平均价格,而不是所有不同的价格加起来 10 美元。
    【解决方案3】:

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

    对于 i 的所有值,您希望变量 includeItem1、...、includeItemN 具有约束 0 ≤ includeItemi ≤ 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 为例。

    【讨论】:

    • 谢谢乔纳斯。您能否对变量和约束进行更多设置。我以前从未这样做过,因此将不胜感激任何额外的帮助
    • 每个项目有三个变量:一个是{0, 1},表示该变量是否包含在您的集合中;第二是物品的价格,第三是能量。然后,您希望最大化“包含”乘以“能量”的线性组合,受制于其他两个 linr 组合的上限
    • s/variable/item/ at "被包含在你的集合中"
    【解决方案4】:

    这听起来很像背包问题。有多种方法(例如,按能量密度降序排列)。

    【讨论】:

      【解决方案5】:

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

      按照能源/价格比对物品进行排序,挑选 100% 最高的物品,直到用完钱,然后从剩余的最高物品中挑选一个分数。

      例如,如果价格为 4,3,5,4,能量为 3,5,2,7,则排序为

      7/4、5/3、3/4、2/5

      因此,您将选择价格为 7 美元的项目 4 和 2,剩余的 3 美元您将以 3 美元的价格和 3*.75 = 2.25 的能量购买第一个项目的 75%

      这将提供 14.25 的总能量

      请注意,允许小数值比只允许 0% 或 100% 给您更高的目标值,因此没有整数解决方案会比 14.25(或 14 更好,因为目标值必须是整数) .

      要解决最初的背包问题,您可以使用分支定界,它在实践中应该可以正常工作。假设您有一个目标值为 z* 的当前候选解决方案

      1. 解决松弛问题,其中允许分数权重。如果值小于 z*,则丢弃该分支。
      2. 计算一个新值 z,它是在没有最后一个分数权重的情况下找到的解决方案,如果这个值大于 z* ,请将 z* 替换为新值
      3. 选择一项(比如列表中的第一项,最有利可图的一项)并形成两个子问题,一个是您必须将其包含在解决方案中,一个是您不能 strong> 将其包含在解决方案中(这是分支步骤)。
      4. 只要还有要解决的子问题,请选择一个并返回到第 1 步。

      请注意,当您创建必须挑选商品的子问题时,只需从预算中减去其价格并将价值添加到利润中,现在您需要解决的问题较小。

      有关更详细的描述,请查看 Wikipedia 上的 Branch and Bound

      【讨论】:

      • 感谢 Pall,但您不能添加小数积。要么添加,要么不添加。
      • “假装”你能做到这一点仍然很有用,因为它为你提供了一个关于你能做到多好的上限。分支定界总是跟踪最佳整数解,它使用分数解来拒绝不能给你比你已经拥有的更好的分支。
      【解决方案6】:

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

      他们的书The Algorithm Design Manual 包含此类信息,可解决大量问题。

      【讨论】:

        【解决方案7】:

        是的,正如每个人都指出的那样,这是一个复杂的背包问题。不过,这么简单的东西可能就足够了……

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

        【讨论】:

          【解决方案8】:

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

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

          【讨论】:

            【解决方案9】:

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

            【讨论】:

              【解决方案10】:

              在个人项目中做了类似的事情,但在 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
                  }
              ]
              

              【讨论】:

                猜你喜欢
                • 2015-12-16
                • 1970-01-01
                • 1970-01-01
                • 2017-06-07
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2019-11-22
                相关资源
                最近更新 更多