三维装箱问题背景
这里主要借鉴了《Three-dimensional bin packing problem with variable bin height》1里的模型。
问题描述
解决的是3D装箱问题的一个变种。不考虑carton的重量约束,仅仅考虑长宽高,方向可变,使得能装进尽可能多的东西。
主体:
- carton :被装入bin中的小纸盒,每个都是规则的长方体,长宽高体积确定,放入bin中必须和坐标轴平行;
- bin: 包含carton的大纸盒,模型中假设底面积不变,高度是随着放入carton数量而变化的;
参数设置
- ( l i , w i , h i ) (l_i,w_i,h_i) (li,wi,hi) :每个carton的长,宽,高。
- ( L , W , H ^ ) (L,W,\hat{H}) (L,W,H^) :Bin的长,宽,和高(这里是决策变量)。
- ( x i , y i , z i ) (x_i,y_i,z_i) (xi,yi,zi) :连续决策变量,carton i 的左下后方点的坐标
- X l i , Z l i , Y w i , Z h i X_{l_i},Z_{l_i},Y_{w_i},Z_{h_i} Xli,Zli,Ywi,Zhi :布尔决策变量,分别表示carton i 的长是否平行于bin的X轴,Z轴,宽是否平行于bin的Y轴,高是否平行于bin的Z轴
- a i j , b i j , c i j a_{ij},b_{ij},c_{ij} aij,bij,cij :布尔变量,分别表示carton i和carton j的相对位置:若i在j的前面,右面,上面,则为1,否则为0;
- M M M:足够大的常数
模型和约束
目标函数是最小化bin的高度
H
^
\hat{H}
H^:
约束条件:
约束(1):保证两个carton不会互相重叠;
约束(2):保证所有carton都在bin之中;
约束(3):限制任意两个carton的相对位置;
约束(4):确保控制carton位置的布尔变量得到控制;
案例参数
这里采用了论文2中SM00案例,其中Bin的长和宽分别为80,58。
共有10个carton。
每个carton的具体参数如下:
carton | 长 | 宽 | 高 |
---|---|---|---|
1 | 36 | 36 | 36 |
2 | 59 | 39 | 20 |
3 | 54 | 40 | 21 |
4 | 58 | 37 | 21 |
5 | 52 | 33 | 20 |
6 | 40 | 31 | 21 |
7 | 31 | 31 | 17 |
8 | 31 | 17 | 16 |
9 | 26 | 23 | 14 |
10 | 33 | 21 | 4 |
C++代码
这里C++调用CPLEX需要配置环境,不知道如何配置的可以参考我同门的这篇博文C++调用Cplex(VS2019 避免每次新建项目重复配置属性)
#include <ilcplex/ilocplex.h>
#include <stdio.h>
using namespace std;
ILOSTLBEGIN
typedef IloArray<IloIntVarArray> IntVarMatrix;
typedef IloArray<IloNumArray> NumMatrix;
typedef IloArray<IloBoolVarArray> BoolVarMatrix;
const int N_CARTONS = 10;
const int M = 1000;
IloInt i;
IloInt j;
//纸箱的长宽高
int CARTON_SIZE[N_CARTONS][3] =
{
{36 ,36, 36},
{59, 39, 20},
{54, 40, 21},
{58 ,37, 21},
{52, 33, 20},
{40, 31, 21},
{31, 31, 17},
{31, 17, 16},
{26, 23, 14},
{33, 21, 4},
};//纸盒尺寸
int bin[2] = { 80,58 };
int main(void*) {
IloEnv env;
IloModel model(env) ;//在现有的env环境下构造名为model的建模对象
//定义决策变量
IloNumVarArray x(env,N_CARTONS);
IloNumVarArray y(env, N_CARTONS);
IloNumVarArray z(env, N_CARTONS);
for ( i = 0; i < N_CARTONS; i++) {
x[i]= IloNumVar(env);
y[i]=IloNumVar(env);
z[i]=IloNumVar(env);
}
IloNumVarArray xl(env, N_CARTONS);
IloNumVarArray zl(env, N_CARTONS);
IloNumVarArray zh(env, N_CARTONS);
IloNumVarArray yw(env, N_CARTONS);
for (i = 0; i < N_CARTONS; i++) {
xl[i]= IloNumVar(env, 0, 1, IloNumVar::Bool);
zl[i]=IloNumVar(env, 0, 1, IloNumVar::Bool);
yw[i]=IloNumVar(env, 0, 1, IloNumVar::Bool);
zh[i]=IloNumVar(env, 0, 1, IloNumVar::Bool);
}
//定义a,b,c
IloArray<IloNumVarArray> a(env, N_CARTONS);
IloArray<IloNumVarArray> b(env, N_CARTONS);
IloArray<IloNumVarArray> c(env, N_CARTONS);
for (i = 0; i < N_CARTONS; i++)
{
a[i] = IloNumVarArray(env, N_CARTONS);
b[i] = IloNumVarArray(env, N_CARTONS);
c[i] = IloNumVarArray(env, N_CARTONS);
for (j = 0; j < N_CARTONS; j++)
{
//if (i == j)continue;
a[i][j] =IloNumVar(env, 0, 1, ILOBOOL);
b[i][j] = IloNumVar(env, 0, 1, ILOBOOL);
c[i][j] = IloNumVar(env, 0, 1, ILOBOOL);
}
}
//定义变量h
IloIntVar h=IloNumVar(env, 0, 100, IloNumVar::Int);
//Create constraints 1
for ( i = 0u; i < N_CARTONS; i++)
{
for ( j = 0u; j < N_CARTONS; j++) {
if (i == j) continue;
model.add(x[i] + CARTON_SIZE[i][0] * xl[i]
+ CARTON_SIZE[i][1] * (zl[i] - yw[i] + zh[i])
+ CARTON_SIZE[i][2] * (1 - xl[i] - zl[i] + yw[i] - zh[i]) <=x[j]+M*(1-a[i][j]));
model.add(y[i] + CARTON_SIZE[i][1] * yw[i]
+ CARTON_SIZE[i][0] * (1 - xl[i] - zl[i])
+ CARTON_SIZE[i][2] * (xl[i] + zl[i] - yw[i]) <= y[j]+M * (1 - b[i][j]));
model.add(z[i] + CARTON_SIZE[i][2] * zh[i]
+ CARTON_SIZE[i][1] * (1 - zl[i] - zh[i])
+ CARTON_SIZE[i][0] * zl[i] <= z[j]+M * (1 - c[i][j]));
model.add(a[i][j] + b[i][j] + c[i][j] +a[j][i] + b[j][i] + c[j][i] >= 1);
}
}
//Create constraints 2-4
for ( i = 0u; i < N_CARTONS; i++)
{
model.add(x[i] + CARTON_SIZE[i][0] * xl[i]
+ CARTON_SIZE[i][1] * (zl[i] - yw[i] + zh[i])
+ CARTON_SIZE[i][2] * (1 - xl[i] - zl[i] + yw[i] - zh[i]) <= bin[0]);
model.add(y[i] + CARTON_SIZE[i][1] * yw[i]
+ CARTON_SIZE[i][0] * (1 - xl[i] - zl[i])
+ CARTON_SIZE[i][2] * (xl[i] + zl[i] - yw[i]) <= bin[1]);
model.add(z[i] + CARTON_SIZE[i][2] * zh[i]
+ CARTON_SIZE[i][1] * (1 - zl[i] - zh[i])
+ CARTON_SIZE[i][0] * zl[i] <= h);
model.add(xl[i] + zl[i] <= 1);
model.add(zl[i] + zh[i] <= 1);
model.add(zl[i] -yw[i] + zh[i] <= 1);
model.add(zl[i] - yw[i] + zh[i] >= 0);
model.add(1-xl[i] - zl[i]+yw[i] - zh[i] >= 0);
model.add(1 - xl[i] - zl[i] + yw[i] - zh[i] <=1);
model.add(xl[i] + zl[i] - yw[i] <= 1);
model.add(xl[i] + zl[i] - yw[i] >= 0);
}
// Create objective function
IloObjective obj(env, h, IloObjective::Minimize);
// Add the objective function to the model
model.add(obj);
IloCplex cplex(model);
bool solved = false;
cplex.setParam(cplex.TiLim, 60);
try {
// Try to solve with CPLEX (and hope it does not raise an exception!)
solved = cplex.solve();
}
catch (const IloException& e) {
std::cerr << "\n\nCPLEX Raised an exception:\n";
std::cerr << e << "\n";
env.end();
throw;
}
if (solved) {
// If CPLEX successfully solved the model, print the results
std::cout << "\n\nCplex success!\n";
std::cout << "\tStatus: " << cplex.getStatus() << "\n";
std::cout << "\tObjective value: " << cplex.getObjValue() << "\n";
for (i = 0u; i < N_CARTONS; i++)
std::cout << "\tCARTONS value:" << cplex.getValue(x[i]) << " " << cplex.getValue(y[i])<<" "<< cplex.getValue(z[i])<<"\n"
<< "\tCARTONS parallel:" << cplex.getValue(xl[i]) << " " << cplex.getValue(zl[i]) << " " << cplex.getValue(zh[i])<<" "<< cplex.getValue(yw[i])<<"\n";
}
else {
std::cerr << "\n\nCplex error!\n";
std::cerr << "\tStatus: " << cplex.getStatus() << "\n";
std::cerr << "\tSolver status: " << cplex.getCplexStatus() << "\n";
}
env.end();
return 0;
}
运行结果
这里找到了最优解,求解时间8秒,bin的最小高度为68。
下面的都是每个carton的坐标和与坐标轴的相对位置信息。
可视化图
将输出的参数转换为可视化图,图是使用python中的三维柱状图画的:
总结
- 第一次写文章,很多东西不知道咋用。
- 希望后面还会写第二次第三次第四次。。
Wu, Y., et al. (2010). “Three-dimensional bin packing problem with variable bin height.” European Journal of Operational Research 202(2): 347-355. ↩︎
He, Y., et al. (2012). “A global search framework for practical three-dimensional packing with variable carton orientations.” Computers & Operations Research 39(10): 2395-2414. ↩︎