文章目录
一、Cplex求解器的JavaAPI语法
1、声明求解模型
首先声明求解模型:
IloCplex model = new IloCplex();
2、定义决策变量(IloNumVar)
2.1 定义一个浮点型的决策变量
通过"IloNumVarType.Float
"表示连续变量:
// 定义一个浮点型的决策变量
IloNumVar x = model.numVar(lb, ub, IloNumVarType.Float, "x");
- lb:lower bound,变量的下届
- ub:upper bound,变量的上届
- 变量类型:
IloNumVarType.Float
:连续变量- “x”:变量的名字,可选择的项
2.2 定义整形决策变量
整形变量通过IloNumVarType.Int
表示,语法如下:
// 写法1:定义一个整型的决策变量
IloNumVar x = model.numVar(0, 10, IloNumVarType.Int, "x");
// 写法2:以上写法等价于如下写法,从源码来看,model.intVar()方法内部还是调用了model.numVar()方法,因此两种写法等价,但此时不在需要传参IloNumVarType.Int:
IloIntVar x = model.intVar(0, 10, "x");
2.3 定义0-1决策变量
0-1变量通过IloNumVarType.Bool
进行定义:
0-1决策变量,本质上还是整形变量,因此根据上面写法有:
IloNumVar x = model.numVar(0.0, 1.0, IloNumVarType.Bool);
还有另一种简单写法,即boolVar()
方法,查看源码,内部也是对numVar()
方法的调用。
IloNumVar x = model.boolVar();
2.4 定义多个决策变量
需要一下子定义多个变量,如需要解决10名护士未来7天3班倒的排班问题,需定义 x i j k ∈ { 0 , 1 } x_{ijk} \in \{0,1\} xijk∈{0,1},=1表示第i名护士在第j天上k班次,就需要按如下方式定义:
IloNumVar[][][] x = new IloNumVar[10][7][3];
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 7; j++) {
for (int k = 0; k < 3; k++) {
x[i][j][k] = model.boolVar();
}
}
}
对于所有这些构造方法,还存在用于一次性创建完整的建模变量数组的等效方法。这些方法称为 numVarArray、intVarArray 和 boolVarArray。
IloNumVar[] x = cplex.numVarArray(3, lb, ub, varname); // 定义优化变量:IloNumVar,3维,以及对应的边界
3、建立表达式(IloNumExpr)
表达式可以用来构建约束和目标函数。
IloNumExpr expr = cplex.sum(x1, cplex.prod(2.0, x2));
常用方法除sum
外,还有prod
,diff
,negative
,square
建立线性表达式时,可以使用更方便的IloLinearNumExpr:
IloLinearNumExpr lin = cplex.linearNumExpr();
for (int i = 0; i < num; ++i)
lin.addTerm(value[i], variable[i]);
上面for循环是数字的标量乘积(scalar product of an array),可利用scalProd
方法重新写为:
IloLinearNumExpr lin = cplex.scalProd(value, variable);
4、约束条件(IloRange)
添加约束的形式如下:
IloRange rng = cplex.range(lb, expr, ub, "name");
当确定了表达式(expression)和右端项(rhs),可以使用IloModeler直接创建范围约束,如expr ≤ 1.0可以通过如下形式创建:
IloRange le = cplex.le(expr, 1.0);
5、目标函数(IloObjective)
目标函数表达式是IloNumExpr
,目标函数构造器如下:
IloObjective obj = cplex.objective(sense, expr, name);
其中:
- sense 是IloObjectiveSense类型,可以去两个值:IloObjectiveSense.Maximize or IloObjectiveSense.Minimize.
- expr 是IloNumExpr类型
- name:目标函数名称,String类型
为方便起见,提供了 Maximize 和 Minimize 方法来分别创建最大化或最小化目标,而不使用 IloObjectiveSense 参数,目标函数对象的名称是可选的。
IloObjective obj = cplex.add(cplex.maximize(expr));
6、获取求解结果
- 通过调用 cplex.solve()判断模型是否求解成功, 当返回 true 时,求解成功,但找到的解决方案可能不是最优的,例如,优化可能会因为遇到迭代限制而提前终止,此时可以使用返回 IloCplex.Status 对象的 getStatus 方法查询求解状态。
- 通过double objval = cplex.getObjValue();获取目标函数值。
- 通过double x1 = cplex.getValue(var1);获取决策变量的值,通常,需要一次性获取所有决策变量的解,无需实现循环,只需调用一个函数即可使用double[] x = cplex.getValues(vars);方法来完成此操作。
- 使用方法 IloCplex.getSlack 或 IloCplex.getSlacks 获取模型中约束的松弛值。
二、使用Cplex求解线性规划问题
运筹优化求解器构模模型并求解的基本步骤如下:
- 创建求解模型
- 添加模型参数
- 构建规划模型:
- 添加决策变量
- 添加约束条件
- 设置目标函数
- 设置求解参数并求解
- 获取求解结果
Cplex提供的例题,在C:\Users\Username\Documents\IBM\ILOG\CPLEX_Studio1263\cplex\examples\src\java\LPex1.java路径下
max
x
1
+
2
x
2
+
3
x
3
subject to
−
x
1
+
x
2
+
x
3
≤
20
x
1
−
3
x
2
+
x
3
≤
30
0
≤
x
1
≤
40
,
x
2
,
x
3
≥
0
\begin{align} \max \quad & x_1+2x_2+3x_3 \\ \text{subject to} \quad & -x_1+x_2+x_3 \leq 20 \\ \quad & x_1-3x_2+x_3 \leq 30 \\ \quad & 0 \leq x_1 \leq40,x_2,x_3\geq0 \end{align}
maxsubject tox1+2x2+3x3−x1+x2+x3≤20x1−3x2+x3≤300≤x1≤40,x2,x3≥0
Cplex对于模型构建有两种视角:
- 可以按行建模,首先输入变量,然后添加对变量和目标函数的约束。
- 通过构造一系列空约束,然后将变量插入到约束和目标函数中,可以按列对问题进行建模。
事实上,甚至可以在同一个程序中混合使用这两种方法.在列生成算法中,需要迭代寻找新的变量,加入到约束中,就可以使用按列建模(Modeling by columns)
2.1按行建模(build by rows)
import ilog.concert.IloException;
import ilog.concert.IloNumVar;
import ilog.cplex.IloCplex;
public class Main {
public static void main(String[] args) {
// cplex求解,一般都使用try...catch...
try {
// 1、声明cplex优化模型
IloCplex cplex = new IloCplex();
System.out.println(cplex);
// 2、模型参数
double[] lb = {0.0, 0.0, 0.0}; // 下限
double[] ub = {40.0, Double.MAX_VALUE, Double.MAX_VALUE}; // 上限
String[] varname = {"x1", "x2", "x3"};
double[][] coeff = {
{-1.0, 1.0, 1.0},
{1.0, -3.0, 1.0}
}; // 约束条件的系数
double[] objvals = {1.0, 2.0, 3.0}; // 目标函数系数
double[] rhs = {20.0, 30.0};
// 3、模型
// 添加决策变量
IloNumVar[] x = cplex.numVarArray(3, lb, ub, varname); // 定义优化变量:IloNumVar,3维,以及对应的边界
// 设定约束条件
for (int i = 0; i < coeff.length; i++) {
cplex.addLe(cplex.scalProd(coeff[i], x), rhs[i]);
}
// 设定目标函数
cplex.addMaximize(cplex.scalProd(x, objvals)); // 定义目标函数:addMaximize最大化,scalProd,连乘
// 5、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);
}
}
}
2.2按列建模(build by columns)
在现有模型中插入一个新的决策变量(Modeling by Column,列生成)。
如:IloColumn col = cplex.column(obj, 1.0).and(cplex.column(rng, 2.0))
;
在目标函数加入决策变量系数为1.0,约束中加入决策变量系数为2.0
增加该决策变量取值范围:IloNumVar var = cplex.numVar(col, 0.0, 1.0);
import ilog.concert.*;
import ilog.cplex.IloCplex;
public class ByCol {
public static void main(String[] args) throws IloException {
IloCplex model = new IloCplex();
IloObjective obj = model.addMaximize();
IloRange[] rng = new IloRange[2];
rng[0] = model.addRange(-Double.MAX_VALUE, 20.0, "c1");
rng[1] = model.addRange(-Double.MAX_VALUE, 30.0, "c2");
IloRange r0 = rng[0];
IloRange r1 = rng[1];
IloNumVar[] x = new IloNumVar[3];
x[0] = model.numVar(model.column(obj, 1.0).and(model.column(r0, -1.0).and(model.column(r1, 1.0))), 0.0, 40.0, "x1");
x[1] = model.numVar(model.column(obj, 2.0).and(model.column(r0, 1.0).and(model.column(r1, -3.0))), 0.0, Double.MAX_VALUE, "x2");
x[2] = model.numVar(model.column(obj, 3.0).and(model.column(r0, 1.0).and(model.column(r1, 1.0))), 0.0, Double.MAX_VALUE, "x3");
if (model.solve()) {
// cplex.output(),数据输出,功能类似System.out.println();
model.output().println("Solution status = " + model.getStatus()); // cplex.getStatus:求解状态,成功则为Optimal
// cplex.getObjValue():目标函数的最优值
model.output().println("Solution value = " + model.getObjValue());
// cplex.getValues(x):变量x的最优值
double[] val = model.getValues(x);
for (int j = 0; j < val.length; j++)
model.output().println("x" + (j + 1) + " = " + val[j]);
}
// 退出优化模型
model.end();
}
}
参考: