我看了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)]。在这里,我不能预算是双重不整数
答案 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>
根据您的更新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
解决方案。请参阅this和this。
答案 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)
您的问题的答案取决于几个因素:
如果你有一个非常大的约束值(远远超过数百万)和非常多的项目(远远超过数千)
然后唯一的选择是Greedy approximation algorithm。按每单位重量的值递减顺序对项目进行排序,并按此顺序打包。
如果您想使用简单算法而不需要高精度
然后再次尝试使用贪心算法。 “满意度值”本身可能是非常粗略的近似,所以当简单逼近可能就足够时,为什么要发明复杂的解决方案呢。
如果您的约束值非常大(甚至连续),但项目数量非常少(少于数千)
然后使用分支定界方法。您无需从头开始实施。试试GNU GLPK。它的分支切割求解器并不完美,但可能足以解决小问题。
如果约束值和项目数都很小
使用任何方法(DP,分支和绑定,或只是暴力)。
如果约束值非常小(少于数百万)但有太多(如数百万)项
然后DP算法是可能的。
最简单的情况是无界背包问题,当每种商品的副本数量没有上限时。 This wikipedia article包含了如何简化问题的详细说明:Dominance relations in the UKP以及如何解决问题:Unbounded knapsack problem。
0-1背包问题比较困难,因为你可以将每种物品打包零次或一次。并且有界背包问题,允许将每种项目打包到某个整数限制时间甚至更加困难。 Internet为这些问题提供了大量实现,same article中有几个建议。但我不知道哪一个好或坏。