具有多个约束的组合优化

Combinatorial optimization with multiple constraints

我有一个问题涉及游戏中的装备优化,但我会用这个例子来简化它:


我想最大化我携带的所有包的价格,但我有以下限制:

  1. 每个袋子最多可容纳 6 件物品。也可以为空。
  2. 所有 4 件行李的总重量不得超过 700 公斤。
  3. 每个袋子都有最小重量值,即使是空袋子也按50kg计算(一个袋子里装1件25kg的东西还是算50kg,装1件51kg的东西还是算51kg)。

物品数量范围为200~300,最多可以选择6*4=24个物品,无法暴力破解

还有其他因素,但不属于组合问题,可以通过简单的编程来解决



这是什么问题?它是线性规划的子集吗?
你知道我可以研究什么样的算法来解决这个问题吗?

我开始阅读有关线性规划的内容,但我在理解某些符号时遇到了问题。我有编程经验,但不涉及数学。

更新


我调查了一下,现在我知道这是一个多维或多项选择的背包问题。解决了简单的背包问题,现在我只剩下 1 个约束条件,即 6 个项目限制。

有人知道解决这个问题的好方法吗?

更新 2


我现在正在使用 GLPK 对这个问题建模并解决它,我非常接近完成它,但是我被一个简单的约束所困。

# Size of knapsack
param s;
# Total of items
param t;
# Type of items
set Z;
# Min bag
param m;

# Items: index, size, profit, count, type
set I, dimen 5;

# Indices
set J := setof{(i,s,p,o,z) in I} i;

# Assignment
var a{J}, binary;

#maximize profit
maximize obj :
  sum{(i,s,p,o,z) in I} p*a[i];

/*s.t. size :
  sum{(i,s,p,o,z) in I} s*a[i] <= c;*/

#constraint of total weight, but with the min value for each bag
#the min function doesn't work, it says argument for min has invalid type
#something about it not being a linear function
s.t. size :
  sum{zz in Z} ( 
    min(m, sum{(i,s,p,o,z) in I: zz=z} (s*a[i]) )
  ) <= c;

#constraint of number of items in each bag, i put an extra count number
#in the set so i could sum it and make it a constraint
s.t. count{zz in Z}:
  sum{(i,s,p,o,z) in I: zz=z} o*a[i] <= t;

solve;

printf "The bag contains:\n";
printf {(i,s,p,o,z) in I: a[i] == 1} " %i", i;
printf "\n";

data;

#set of type of items
set Z := 1 2 3 4;

# Total weight limit
param c := 100;
# Only 2 items per bag
param t := 2;
# Min bag value, if the bag weights less than it, then it counts as this value
param M := 10;

# Items: index, size, profit, count, type
set I :=
  1 10 10 1 1 
  2 10 10 1 1
  3 15 45 1 2
  4 20 20 1 2
  5 20 20 1 3
  6 24 24 1 3
  7 24 25 1 4
  8 50 50 1 4;

end;

注意:我在这里使用了不同的值以使其较小。

这是我的模型,它在没有最小重量限制的情况下工作,我只需要它求和 50 公斤的最小值或行李总数,但 min 函数在那里不起作用。我试过这个公式

(无法 post 图片)

min(a,b) = (a+b- abs(a-b))/2

但是我也不会使用abs函数

有人能为我指出正确的方向吗?

如果您正在寻找一个精确的最优答案,那么该问题是 NP 完全问题,但是,我注意到您描述的问题集非常小。

如果您的问题很小,那么蛮力搜索是可行的:您可以生成所有可能的解决方案,计算解决方案成本,然后选择最佳解决方案。

如果您的示例仅打算概述问题描述,而您的现实生活中的问题集实际上是 "large",则暴力破解将不起作用,因为执行时间会随着数量的增加呈指数增长项目。这是一个相当 old paper on this problem.

一个有趣的通知是,you can transform your constraints into numerical "penalties" 并使用不受约束的优化技术,稍微简化了您的问题。

保证最优解的算法仍然需要启发式修剪 bad/unfeasible 和 "dominated" 快速解决方案。

根据我读过的研究,即使使用启发式修剪,这种方法对于超过相对较小的集合来说实际上是不可行的 "paper about 2 phase" approach

典型的元启发式优化技术适用于较大的集合,特别是 Simulated Annealing、禁忌搜索、群体。

您可以将您的问题表述为 0-1 整数规划,并使用 GLPK 或其他线性规划求解器(glpk 是免费的)求解。唯一的问题:你带的是空袋子吗? 让

i = {1,2,3,4} -- set of item types
j -- set of items
x_ij = {0,1} -- decision variable for jth item of ith type
w_ij and p_ij -- weights and profits

max sum_ij p_ij*x_ij

subject to:
sum_j x_ij <= 6 for all i  // at most 6 items in a bag
// if you need empty bags:
sum_ij w_ij*x_ij + 4*50 <= 700 kg  // total weight

x_ij = {0,1} for all i and j.

如果你不需要空袋那么配方就变得更复杂了

max sum_ij p_ij*x_ij

subject to:
sum_j x_ij <= 6    for all i  // at most 6 items in a bag
sum_ij w_ij*x_ij + 50*(sum_i y_i) <= 700 kg  // total weight
y_i <= sum_j x_ij    for all i  
N*y_i >= sum_j x_ij    for all i  // N -- total number of items of ith type, 
                                  // fixed value, not a variable; e.g., 
                                  // you have 10 items of each kind then N=10

y_i = {0,1} for all i
x_ij = {0,1} for all i and j.

如果你需要解决一次程序那么我建议写一个cplex .lp文件http://www-01.ibm.com/support/knowledgecenter/SS9UKU_12.4.0/com.ibm.cplex.zos.help/FileFormats/topics/LP.html并用glpsolve解决它;否则使用 c++ 或 python glpk 可调用库。

更新 1:

可以分两步线性化约束 min{50, sum_i x_i*w_i}。为简单起见,考虑一个背包。让

M -- the total possible weight (sum up all input items' weights)
y={0,1} -- when sum_i x_i*w_i >= 50 y=1, otherwise y=0

min{50, sum_i x_i*w_i} <= 100    is equivalent to
(0): (1-y)*50 + y*(sum_i x_i*w_i) <= 100
(1): My >= sum_i x_i*w_i - 50
(2): M(1-y) >= 50 - sum_i x_i*w_i

这有点不寻常,但它是正确的。实际上,当 sum_i x_i*w_i >= 50 时,求解器必须通过约束 (1)y 设置为 1,同时 (2) 只是 0 >= 50-sum_i x_i*w_i <= 0,即是多余的;当 sum_i x_i*w_i < 50、y=0 by (2) 和现在 (1) 是多余的。

下一步是线性化 (0) 中的第二项。让z_i = {0,1},它将代表y*x_i项。请注意 y*x_i 只有当两项都为 1 时才为 1,否则为 0。因此,

y*(sum_i x_i*w_i)  is equivalent to

sum_i z_i*w_i
2*z_i <= x_i + y,  for all i
z_i >= x_i + y - 1,  for all i

和以前一样,只有当x_i=y=1 时,很容易检查z_i = 1,否则为0。

线性化后您的约束如下所示:

(0): (1-y)*50 + (sum_i z_i*w_i) <= 100

(1): My >= sum_i x_i*w_i - 50
(2): M(1-y) >= 50 - sum_i x_i*w_i

(3): 2*z_i <= x_i + y,  for all i
(4): z_i >= x_i + y - 1,  for all i

 y = {0,1}, z_i = {0,1}

为了处理 50 公斤的行李限制,您必须为每个行李的重量创建一个变量,W_j

W_j >= 50  for all j
W_j >= sum_i w_i * X_ij for all j
sum_j W_j <= 700

这样一来,行李的重量始终至少为 50 公斤,但如果物品的重量超过 50 公斤(根据您的问题定义)。

祝你好运:)