目录
引言
cplex是求解线性/整数规划问题的高效求解器,而java是极其常用的程序开发语言。本文力争通过两个优化实例,讲解清楚在java语言下,如何调用cplex高效求解线性/整数规划问题。
简单的一维线性规划实例
针对简单的实例,网上已有实例,此处就直接做搬运工了:java调用cplex。
优化实例
m a x x 1 + 2 x 2 + 3 x 3 s . t . − x 1 + x 2 + x 3 ≤ 20 x 1 − 3 x 2 + x 3 ≤ 40 0 ≤ x 1 ≤ 40 max \qquad\ x_1+2x_2+3x_3 \\s.t. \qquad\ \qquad\ \qquad\ \qquad\ \\ \qquad\ -x_1+x_2+x_3 \le20 \\ \qquad\ x_1-3x_2+x_3 \le40 \\ \qquad\ 0 \le x_1 \le 40 max x1+2x2+3x3s.t. −x1+x2+x3≤20 x1−3x2+x3≤40 0≤x1≤40
cplex求解
import ilog.concert.IloException;
import ilog.concert.IloNumVar;
import ilog.cplex.IloCplex;
public class CplexExamples {
public static void main(String[] args) {
// cplex求解,一般都使用try...catch...
try {
// 声明cplex优化模型
IloCplex cplex = new IloCplex();
// 设定变量上下限
double[] lb = {0.0, 0.0, 0.0}; // 下限
double[] ub = {40.0, Double.MAX_VALUE, Double.MAX_VALUE}; // 上限
IloNumVar[] x = cplex.numVarArray(3, lb, ub); // 定义优化变量:IloNumVar,3维,以及对应的边界
// 设定目标函数
double[] objvals = {1.0, 2.0, 3.0}; // 目标函数系数
cplex.addMaximize(cplex.scalProd(x, objvals)); // 定义目标函数:addMaximize最大化,scalProd,连乘
// 设定约束条件
double[] coeff1 = {-1.0, 1.0, 1.0}; // 第一组约束条件的系数
double[] coeff2 = {1.0, -3.0, 1.0}; // 第二组约束条件的系数
cplex.addLe(cplex.scalProd(x, coeff1), 20.0); // 定义第一组约束条件的系数,addLe:括号中左边的表达式小于等于右边
cplex.addLe(cplex.scalProd(x, coeff2), 30.0); // 定义第二组约束条件的系数
// cplex.solve():模型求解
if (cplex.solve()) {
// cplex.output(),数据输出,功能类似System.out.println();
cplex.output().println("Solution status = " + cplex.getStatus()); // cplex.getStatus:求解状态,成功则为Optimal
// cplex.getObjValue():目标函数的最优值
cplex.output().println("Solution value = " + cplex.getObjValue());
// cplex.getValues(x):变量x的最优值
double[] val = cplex.getValues(x);
for (int j = 0; j < val.length; j++)
cplex.output().println("x" + (j+1) + " = " + val[j]);
}
// 退出优化模型
cplex.end();
} catch (IloException e) {
System.err.println("Concert exception caught: " + e);
}
}
}
个人理解
(1)以上实例亲测有效,可以作为程序校验的标准,用于检测cplex环境是否已经成功搭建。
(2)几乎针对每一行代码,都增加了详细的注释,对小白们更加友好。
(3)该实例不便于直接改写为大规模优化问题的求解,原因包括:多维变量如何表达,没有提及;cplex.scalProd难以直接扩展使用到复杂的约束条件和目标函数等。
(4)实例中未涉及如何高效调试cplex代码,不便于算法开发和维护。
复杂的二维整数规划实例
优化实例
a = [8 7 9 4 5
8 0 7 4 4
4 3 3 8 7
7 0 10 8 7
1 1 0 2 8]
b = [3 5 8 10 9
7 10 2 6 2
7 3 5 1 8
1 6 7 1 2
1 2 9 2 10]
存在以上两个二维矩阵,要求分别从两个矩阵中的每一行,选出一个元素,使得总的数值最小化,约束条件为a和b中,相同行所选定的元素对应的列值之和,小于7。
针对以上问题,定义第一组优化变量为
x
i
,
j
=
0
,
1
,
i
=
0
,
1
,
2
,
3
,
4
,
j
=
0
,
1
,
2
,
3
,
4
x_{i,j}=0,1,i=0,1,2,3,4, j=0,1,2,3,4
xi,j=0,1,i=0,1,2,3,4,j=0,1,2,3,4
其中,
x
i
,
j
=
1
x_{i,j}=1
xi,j=1表示取a中第
i
i
i行第
j
j
j列的值;反之,表示不取。
定义第二组优化变量为
y
i
,
j
=
0
,
1
,
i
=
0
,
1
,
2
,
3
,
4
,
j
=
0
,
1
,
2
,
3
,
4
y_{i,j}=0,1,i=0,1,2,3,4, j=0,1,2,3,4
yi,j=0,1,i=0,1,2,3,4,j=0,1,2,3,4
其中,
y
i
,
j
=
1
y_{i,j}=1
yi,j=1表示取a中第
i
i
i行第
j
j
j列的值;反之,表示不取。
目标函数可以描述为
m
i
n
∑
i
=
0
4
∑
j
=
0
4
(
a
i
,
j
x
i
,
j
+
b
i
,
j
y
i
,
j
)
min \sum_{i=0}^4\sum_{j=0}^4(a_{i,j}x_{i,j}+b_{i,j}y_{i,j})
mini=0∑4j=0∑4(ai,jxi,j+bi,jyi,j)
约束条件包括:针对任意
i
i
i和
j
j
j
x
i
,
j
=
y
i
,
j
x_{i,j}=y_{i,j}
xi,j=yi,j
针对每一个
i
i
i
∑
j
=
0
4
x
i
,
j
=
1
,
∑
j
=
0
4
y
i
,
j
=
1
\sum_{j=0}^4x_{i,j}=1, \sum_{j=0}^4y_{i,j}=1
j=0∑4xi,j=1,j=0∑4yi,j=1
cplex求解
import ilog.concert.IloException;
import ilog.concert.IloIntVar;
import ilog.concert.IloLinearNumExpr;
import ilog.cplex.IloCplex;
public class CplexExamples {
public static void main(String[] args) {
int[][] a = {{8, 7, 9, 4, 5}, {8, 0, 7, 4, 4}, {4, 3, 3, 8, 7}, {7, 0, 10, 8, 7}, {1, 1, 0, 2, 8}};
int[][] b = {{3, 5, 8, 10, 9}, {7, 10, 2, 6, 2}, {7, 3, 5, 1, 8}, {1, 6, 7, 1, 2}, {1, 2, 9, 2, 10}};
try {
// 声明cplex优化模型
IloCplex model = new IloCplex();
// 定义两个二维优化变量,使用for循环确定上下限
IloIntVar[][] x = new IloIntVar[5][5];
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
x[i][j] = model.intVar(0, 4, "x[" + i + "," + j + "]");
}
}
IloIntVar[][] y = new IloIntVar[5][5];
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
y[i][j] = model.intVar(0, 4, "y[" + i + "," + j + "]");
}
}
// 定义目标函数
IloLinearNumExpr objExpr = model.linearNumExpr();
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
objExpr.addTerm(a[i][j], x[i][j]);
objExpr.addTerm(b[i][j], y[i][j]);
}
}
model.addMinimize(objExpr);
// 定义约束条件
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
// 第一组约束
model.addEq(x[i][j], y[i][j]);
}
// 第二组约束
model.addEq(model.sum(x[i]), 1);
model.addEq(model.sum(y[i]), 1);
}
// 优化计算,输出最优解
if (model.solve()) {
System.out.println("最优解为:" + model.getObjValue());
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
System.out.println("x[" + i + "," + j + "]: " + model.getValue(x[i][j]));
}
}
}
// 退出优化模型
model.end();
} catch (IloException e) {
System.err.println("Concert exception caught: " + e);
}
}
}
注意事项
(1)定义优化变量时,使用for循环的方式,更加灵活。
(2)定义目标函数时,采用IloLinearNumExpr的形式,可以处理复杂的表达式。
(3)定义了变量的简称后,在model.solve前打个断点,可以很容易地看到具体的优化模型,下图给出了目标函数,以及部分的约束条件。
(4)优化无解或者优化解明显有问题时,可以通过控制变量法快速定位问题:逐组删除约束条件,确定是哪一组约束条件导致了错误的结果。
总结
步骤编号 | 具体步骤 | 相关命令 |
---|---|---|
1 | 声明优化模型 | IloCplex cplex = new IloCplex() |
2 | 定义优化变量 | 目标名称:IIloIntVar [][] x = new IloIntVar[][] 变量范围:x[i][j] = model.intVar(0, 1, “x[” + i + “,” + j + “]”) |
3 | 定义目标函数 | 最大化:model.addMaximize() 最小化:model.addMinimize() |
4 | 定义约束条件 | 数值/表达式a,b相等:model.addEq(a,b) 数值/表达式a小于等于b:model.addLe(a,b) 数值/表达式a大于等于b:model.addGe(a,b ) |
3-4 | 3,4中常用命令 | 声明变量表达式:IloLinearNumExpr Expr = model.linearNumExpr() 向量a,b连乘:model.scalProd(a,b) 数值/表达式a,b相加:model.sum(a,b) 数值/表达式a,b相减:model.diff(a,b) 数值/表达式a,b相乘:model.prod(a,b) 表达式c中添加数值a与表达式b的乘积:c.addTerm(a, b) |
5 | 退出优化模型 | cplex.end() |