基于多重约束算法寻找最优值
Posted
技术标签:
【中文标题】基于多重约束算法寻找最优值【英文标题】:Find optimum value based on multiple constraints algorithm 【发布时间】:2016-01-26 14:39:14 【问题描述】:我有以下向量(或 C 矩阵和 V 向量)和 K1、K2、K3 常量
Constraints [c11 + c12 + c13 + ... + c1n] <= K1
[c21 + c22 + c23 + ... + c2n] <= K2
[c31 + c32 + c33 + ... + c3n] <= K3
-------------------------------------------------
Values [ v1 + v2 + v3 + ... + vn] -> Max
作为输入,我得到 C 和 V 的值,作为输出,我想提供仅包含 0 和 1 值的 X 向量,并且给了我
[c11 * x1 + c12 * x2 + c13 * x3 + ... + c1n * xn <= K1
[c21 * x1 + c22 * x2 + c23 * x3 + ... + c2n * xn <= K2
[c31 * x1 + c32 * x2 + c33 * x3 + ... + c3n * xn <= K3
------------------------------------------------------
[ v1 * x1 + v2 * x2 + v3 * x3 + ... + vn * xn] -> Max
作为一个过于简化的例子:
输入:
K1 = 15
K2 = 20
K3 = 10
c1 = [3, 6, 8] | sum(c1 * X) <= 15
c2 = [8, 9, 3] | sum(c2 * X) <= 20
c3 = [7, 5, 2] | sum(c3 * x) <= 10
v = [2, 5, 3] | sum( v * X) -> Max
输出提供 X 向量,该向量使约束内的值最大化:
X = [0, 1, 1]
我正在寻找一种优雅的算法(也可能是 Java 或 C# 实现),根据输入为我提供输出。我们可以假设约束的数量始终为 3,并且提供了 C 和 V(以及 K1、K2、K3)的所有值。
另一个简单的例子是:你有一个房间(3D),所以你的约束是 width、height 和 length房间(K1、K2、K3),并且您有一个家具物品清单(n 个物品)。我所有的家具都有自己的 lenght (c1i)、width (c2i) 和 height (c3i) 和值 (vi)。您想用最有价值的家具物品打包房间,这些家具物品适合房间尺寸。所以输出是一个 n 长的 X 变量,它只包含 0 和 1 值,如果 xi = 1,第 i 个元素被选中进入房间,如果 xi = 0,第 i 个元素不会被选中在房间里。
【问题讨论】:
那是整数线性规划,但如果 X 只有三个元素,每个元素都是 0 或 1,那么尝试所有 8 个元素会简单得多。 这就是为什么我提到我的例子过于简化了。 X 有 n 个元素,与 V 相同。基本上每个 i 元素都有自己的 c1i、c2i、c3i 和 vi 值,并且该元素要么被选中(xi 为 1),要么不被选中(xi 为 0) 向量 X 有多大? 就像我在之前的评论中提到的:X 有 n 个元素,与 V 相同。基本上每个 i 元素都有自己的 c1i、c2i、c3i 和 vi 值,并且元素要么被选中(xi 为 1 ) 与否 (xi 为 0) 您要解决的问题称为multidimensional knapsack problem,请参阅this post 【参考方案1】:这是多维 0-1 背包问题,是 NP-hard。
解决方法的概述可以在here、相对较新的研究论文here 和python
here 中找到遗传算法实现。
取自python实现(上面的链接pyeasyga
)是这个例子:
from pyeasyga import pyeasyga
# setup data
data = [(821, 0.8, 118), (1144, 1, 322), (634, 0.7, 166), (701, 0.9, 195),
(291, 0.9, 100), (1702, 0.8, 142), (1633, 0.7, 100), (1086, 0.6, 145),
(124, 0.6, 100), (718, 0.9, 208), (976, 0.6, 100), (1438, 0.7, 312),
(910, 1, 198), (148, 0.7, 171), (1636, 0.9, 117), (237, 0.6, 100),
(771, 0.9, 329), (604, 0.6, 391), (1078, 0.6, 100), (640, 0.8, 120),
(1510, 1, 188), (741, 0.6, 271), (1358, 0.9, 334), (1682, 0.7, 153),
(993, 0.7, 130), (99, 0.7, 100), (1068, 0.8, 154), (1669, 1, 289)]
ga = pyeasyga.GeneticAlgorithm(data) # initialise the GA with data
ga.population_size = 200 # increase population size to 200 (default value is 50)
# define a fitness function
def fitness(individual, data):
weight, volume, price = 0, 0, 0
for (selected, item) in zip(individual, data):
if selected:
weight += item[0]
volume += item[1]
price += item[2]
if weight > 12210 or volume > 12:
price = 0
return price
ga.fitness_function = fitness # set the GA's fitness function
ga.run() # run the GA
print ga.best_individual() # print the GA's best solution
data
的最后一个维度是价格,另外两个维度是重量和体积。
您可以调整此示例,使其解决二维以上的问题。
希望对你有帮助。
编辑:遗传算法一般不保证找到最优解。对于三个约束,它可能会找到好的解,但不能保证最优。
更新:数学优化解决方案
另一种选择是使用PuLP,这是一种用于数学优化问题的开源建模框架。该框架调用求解器,即专门为解决优化问题而设计的软件。简而言之,框架的工作是将数学问题描述与解决时所需的形式联系起来,而求解器的工作是实际解决问题。
您可以使用例如pip
(pip install pulp
) 安装纸浆。
这里是前面在pulp
中建模的例子,通过修改this例子:
import pulp as plp
# Let's keep the same data
data = [(821, 0.8, 118), (1144, 1, 322), (634, 0.7, 166), (701, 0.9, 195),
(291, 0.9, 100), (1702, 0.8, 142), (1633, 0.7, 100), (1086, 0.6, 145),
(124, 0.6, 100), (718, 0.9, 208), (976, 0.6, 100), (1438, 0.7, 312),
(910, 1, 198), (148, 0.7, 171), (1636, 0.9, 117), (237, 0.6, 100),
(771, 0.9, 329), (604, 0.6, 391), (1078, 0.6, 100), (640, 0.8, 120),
(1510, 1, 188), (741, 0.6, 271), (1358, 0.9, 334), (1682, 0.7, 153),
(993, 0.7, 130), (99, 0.7, 100), (1068, 0.8, 154), (1669, 1, 289)]
w_cap, v_cap = 12210, 12
rng_items = xrange(len(data))
# Restructure the data in dictionaries
items = ['item_'.format(i) for i in rng_items]
weight = items[i]: data[i][0] for i in rng_items
volume = items[i]: data[i][1] for i in rng_items
price = items[i]: data[i][2] for i in rng_items
# Make the problem, declare it as a maximization problem
problem_name = "3D Knapsack"
prob = plp.LpProblem(problem_name, plp.LpMaximize)
# Define the variables
plp_vars = plp.LpVariable.dicts('', items, 0, 1, plp.LpInteger)
# Objective function
prob += plp.lpSum([price[i]*plp_vars[i] for i in plp_vars])
# Constraints
prob += plp.lpSum([weight[i]*plp_vars[i] for i in plp_vars]) <= w_cap
prob += plp.lpSum([volume[i]*plp_vars[i] for i in plp_vars]) <= v_cap
# Solution
prob.solve()
# If you want to save the problem formulation in a file
# prob.writeLP(problem_name + 'lp')
# Each of the variables is printed with it's resolved optimum value
for v in prob.variables():
print v.name, "=", v.varValue
# The optimised objective function value is printed to the screen
print "Total gain = ", plp.value(prob.objective)
目标是 3,540。
如何运行的演示是here。
【讨论】:
这看起来很有希望。谢谢! 确实要强调最后一点:当我运行此 Python 代码时,我得到的目标是 3442。文档报告的目标是 3531。我相信这个问题的最佳解决方案的目标是 3540。 @ErwinKalvelagen 是的。对于小问题,初始化几个随机种子,循环运行并保持最佳结果可能是一个不错的选择。如果我有时间,我会用cbc
发布pulp
公式。我试图将我的答案保留在开源领域:)【参考方案2】:
可以完全解决,例如使用 Gurobi 的 C# 绑定:
var env = new GRBEnv();
var model = new GRBModel(env);
var vars = model.AddVars(v.Length, GRB.BINARY);
model.Update();
GRBLinExpr obj = 0.0;
obj.AddTerms(v, vars, 0, v.Length);
model.SetObjective(obj, GRB.MAXIMIZE);
for (int i = 0; i < 3; i++)
GRBLinExpr expr = 0.0;
for (int j = 0; j < C[i].Length; j++)
expr.AddTerm(C[i][j], vars[j]);
model.AddConstr(expr, GRB.LESS_EQUAL, K[i], "");
model.Update();
model.Optimize();
然后你可以在变量上调用Get(GRB.DoubleAttr.X)
来获取它们的值。
【讨论】:
感谢您的提示!我怎么能运行这个?你有什么教程要设置吗?是免费的吗? @DDan Gurobi 有时是免费的,用于学术用途,但用于商业用途相当昂贵。 GLPK 的工作方式有点不同,但上面的代码很容易适应。以上是关于基于多重约束算法寻找最优值的主要内容,如果未能解决你的问题,请参考以下文章