具有连续(非独特)约束的背包

时间:2012-01-14 07:44:44

标签: algorithm dynamic-programming knapsack-problem

我看了Dynamic Programming - Kapsack Problem (YouTube)。但是,我正在解决一个稍微不同的问题,其中约束是预算,价格,是双倍,而不是整数。所以我想知道如何修改它?双是“连续”不像整数,我可以有1,2,3 ....我不认为我做0.0,0.1,0.2 ......?

更新1

我想过将double转换为int乘以100. Money只有2位小数。但那意味着价值范围会非常大吗?

更新2

我需要解决的问题是:

  

物品有价格(双倍)&满意度(整数)值。我有预算作为约束,我需要最大化满意度。

     

在youtube视频中,作者创建了两个2d数组,如int [numItems] [possibleCapacity(weight)]。在这里,我不能预算是双重不整数

6 个答案:

答案 0 :(得分:16)

如果要使用具有任意精度的浮点数(即,没有固定的小数位数),并且这些不是分数,则动态编程将不起作用。

动态编程的基础是存储特定输入的计算的先前结果。因此,如果您使用具有任意精度的浮点数,则每个可能的浮点数都需要几乎无限的内存,当然,还要进行无限计算,这是不可能的,也是非最优的。

但是,如果这些数字具有固定的精度(与货币一样,只有两个十进制数),您可以通过乘以它们(如您所述)将它们转换为整数,然后将背包问题解决为通常

答案 1 :(得分:5)

您必须执行您在更新1中所说的内容:以美分表示预算和项目价格(假设我们正在谈论美元)。那我们不是在谈论任意精度或连续数字。每个价格(和预算)都是一个整数,只是整数代表美分。

为了简化操作,我们假设预算为10美元。 问题是背包容量必须采取以下所有值:

[0.00, 0.01, 0.02, 0.03, ..., 9.99, 10.00] 

价值观是两个。 SOLUTION MATRIX和KEEP MATRIX的每一行都有1001列,因此无法手动解决问题(如果预算是数百万美元,即使计算机可能会遇到困难) )但这是原始问题所固有的(你无法做任何事情)。

最好的选择是使用一些关于KNAPSACK的现有代码,也可以自己编写代码(我不建议)。

如果您找不到有关KNAPSACK的现有代码且熟悉Linux / Mac,我建议您安装GNU Linear Programming Kit(GLPK)并将问题表达为Integer Linear Program或二进制线性程序(如果你想解决0-1背包)。它将为您解决问题(如果需要,您可以通过C,C ++,Python和Java使用它)。有关使用GLPK的帮助,请检查this awesome article(您可能需要part 2,其中涉及整数变量)。如果您需要有关GLPK的更多帮助,请发表评论。

修改

基本上,我想说的是你的约束不是连续的,它是离散的(美分),你的问题是预算可能太多美分所以你不会能够手工解决它。

不要被吓倒,因为你的预算可能是几美元 - >几百美分。如果您的预算仅为18美分,则问题的大小将与YouTube视频中的大小相当。如果他的背包尺寸为1800(甚至180),视频中的人也无法(手动)解决他的问题。

答案 2 :(得分:3)

这不是您问题的答案,但也可能是您正在寻找的内容:

线性规划

我使用Microsoft's Solver Foundation 3制作了一个简单的代码来解决您描述的问题。它不使用背包算法,而是使用单工方法

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SolverFoundation.Common;
using Microsoft.SolverFoundation.Services;

namespace LPOptimizer
{
    class Item
    {
        public String ItemName { get; set; }
        public double Price { get; set; }
        public double Satisfaction { get; set; }

        static void Main(string[] args)
        {
            //Our data, budget and items with respective satisfaction and price values
            double budget = 100.00;
            List<Item> items = new List<Item>()
            {
                new Item(){
                    ItemName="Product_1", 
                    Price=20.1, 
                    Satisfaction=2.01
                },
                new Item(){
                    ItemName="Product_2", 
                    Price=1.4, 
                    Satisfaction=0.14
                },
                new Item(){
                    ItemName="Product_3", 
                    Price=22.1, 
                    Satisfaction=2.21
                }
            };

            //variables for solving the problem.
            SolverContext context = SolverContext.GetContext();
            Model model = context.CreateModel();
            Term goal = 0; 
            Term constraint = 0; 

            foreach (Item i in items)
            {
                Decision decision = new Decision(Domain.IntegerNonnegative, i.ItemName);
                model.AddDecision(decision); //each item is a decision - should the algorithm increase this item or not?

                goal += i.Satisfaction * decision; //decision will contain quantity.
                constraint += i.Price * decision; 
            }

            constraint = constraint <= budget; //this now says: "item_1_price * item_1_quantity + ... + item_n_price * item_n_quantity <= budget";

            model.AddConstraints("Budget", constraint); 

            model.AddGoals("Satisfaction", GoalKind.Maximize, goal); //goal says: "Maximize: item_1_satisfaction * item_1_quantity + ... + item_n_satisfaction * item_n_quantity"

            Solution solution = context.Solve(new SimplexDirective());
            Report report = solution.GetReport();
            Console.Write("{0}", report);
            Console.ReadLine();
        }
    }
}

这可以找到具有价格(双精度)的项目数(整数)的最佳最大值,具有预算约束(双精度)。

从代码中可以看出,您可以在实际值中使用一些项目数量(双倍)。这可能也会比具有大范围的背包更快(如果你决定使用你提到的* 100)。

您可以轻松指定其他约束(例如某些项目的数量等)。上面的代码改编自this MSDN How-to,其中显示了如何轻松定义约束。

修改

我想到你可能没有使用C#,在这种情况下我相信有许多用于多种语言的线性编程的库,并且使用起来相对简单:你指定约束和目标。 / p>

EDIT2

根据您的更新2 ,我已更新此代码以包含满意度。

答案 3 :(得分:2)

看了this。 对不起,我没有评论权限。

修改1

你是说约束是预算而不是背包重量? 这仍然是一个背包问题。

或者你的意思是项目值而不是整数(0-1背包问题)你的分数。然后贪婪的方法应该没问题。

修改2

如果我理解你的问题,那就说明了

我们有n种项目,1到n。每种商品i的价值为vi,价格为pi。我们通常假设所有的价值和价格都是非负的。预算为B

问题最常见的表述是0-1 knapsack problem,它将每种项目的副本数xi限制为零或一。在数学上,0-1背包问题可以表述为:

         n
maximize E(vi.xi)
         i=i

           n
subject to E(pi.xi) <= B,         xi is a subset of {0,1}
           i=1

Neo Adonis's answer 就在这里。动态编程在实践中不会对任意精度起作用。

但是如果你愿意将精度限制在2位小数位...然后继续按照视频中的说明进行..你的表应该看起来像这样..

+------+--------+--------+--------+--------+--------+--------+
| Vi,Pi| 0.00   | 0.01   | 0.02   | 0.03   | 0.04 ...    B   |
+------+--------+--------+--------+--------+--------+--------+
|4,0.23|        |        |        |        |        |        |
|2,2.93|        |        |        |        |        |        |
|7,9.11|        |        |        |        |        |        |
| ...  |        |        |        |        |        |        |
| Vn,Pn|        |        |        |        |        | answer |
+------+--------+--------+--------+--------+--------+--------+

你甚至可以像你提到的那样将实数转换为int。

是的,值的范围非常大,你还必须理解背包是NP-complete,即没有有效的算法来解决这个问题。仅使用DP的pseudo polynomial解决方案。请参阅thisthis

答案 4 :(得分:2)

答案不太正确。

你可以实现一个动态程序,用整数满意度和任意实数奖励来解决背包问题,比如双打。

首先是整数奖的问题的标准解决方案:

Define K[0..M, 0..n] where K[j, i] = optimal value of items in knapsack of size j, using only items 1, ..., i

for j = 0 to M do K[j,0] = 0
for i = 1 to n do
    for j = 0 to M do
        //Default case: Do not take item i
        K[j,1] = K[j, i-1]
        if j >= w_i and v_i+K[j-w, i-1] > K[j, i] then
            //Take item i
            K[j,i] = v_i + K[j-w_i, i-1]

这会创建一个表,通过跟随条目K [M,n]的递归来找到解决方案。

现在解决实数重量的问题:

Define L[0..S, 0..N] where L[j, i] = minimal weight of items in knapsack of total value >= j, using only items 1, ..., i
and S = total value of all items

for j = 0 to S do L[j, 0] = 0
for i = 0 to n do
    for j = 0 to S do
        //Default case: Do not take item i
        L[j,i] = L[j, i-1]
        if j >= v_i and L[j-v_i, i-1] + w_i < L[j, i] then
            //Take item i
            L[j, i] = L[j -v_i, i-1] + w_i

现在可以找到与其他版本类似的解决方案。我们现在不使用重量作为第一维,而是使用导致最小重量的物品的总价值。 代码具有或多或少相同的运行时O(S * N),而另一个具有O(M * N)。

答案 5 :(得分:1)

您的问题的答案取决于几个因素:

  1. 约束的值有多大(如果缩放到cants并转换为整数)。
  2. 有多少物品。
  3. 要解决什么样的背包问题
  4. 什么是精确度。
  5. 如果你有一个非常大的约束值(远远超过数百万)和非常多的项目(远远超过数千)

    然后唯一的选择是Greedy approximation algorithm。按每单位重量的值递减顺序对项目进行排序,并按此顺序打包。

    如果您想使用简单算法而不需要高精度

    然后再次尝试使用贪心算法。 “满意度值”本身可能是非常粗略的近似,所以当简单逼近可能就足够时,为什么要发明复杂的解决方案呢。

    如果您的约束值非常大(甚至连续),但项目数量非常少(少于数千)

    然后使用分支定界方法。您无需从头开始实施。试试GNU GLPK。它的分支切割求解器并不完美,但可能足以解决小问题。

    如果约束值和项目数都很小

    使用任何方法(DP,分支和绑定,或只是暴力)。

    如果约束值非常小(少于数百万)但有太多(如数百万)项

    然后DP算法是可能的。

    最简单的情况是无界背包问题,当每种商品的副本数量没有上限时。 This wikipedia article包含了如何简化问题的详细说明:Dominance relations in the UKP以及如何解决问题:Unbounded knapsack problem

    0-1背包问题比较困难,因为你可以将每种物品打包零次或一次。并且有界背包问题,允许将每种项目打包到某个整数限制时间甚至更加困难。 Internet为这些问题提供了大量实现,same article中有几个建议。但我不知道哪一个好或坏。