介绍
简单案例
官网给的例子
public class MyTest {
/**
* max z = x1 + 5x2 + 1x3
* -x1 + x2 + x3 <=56
* x1 - 3x2 + x3 <= 39
* 0 <= x1 <= 40 * 0 <= x2, x3
*
* @throws IloException
*/
public static void main(String[] args) throws IloException {
IloNumVarType varType = IloNumVarType.Float;
int varNum = 3;
IloCplex cplex = new IloCplex();
/// 决策变量申明
// 有几个决策变量
IloNumVar[] vars = new IloNumVar[varNum];
// 目标函数里面的决策变量的系数
double[] xishu = new double[]{1, 5, 1};
// 决策变量下界
double[] mins = new double[]{0, 0, 0};
// 决策变量上界
double[] maxs = new double[]{40, Double.MAX_VALUE, Double.MAX_VALUE};
for (int i = 0; i < vars.length; i++) {
vars[i] = cplex.numVar(mins[i], maxs[i], varType);
}
/// 目标函数
cplex.addMaximize(cplex.scalProd(vars, xishu));
/// 约束条件
cplex.addLe(cplex.sum(cplex.prod(-1.0, vars[0]), cplex.prod(1.0, vars[1]), cplex.prod(1.0, vars[2])), 56);
cplex.addLe(cplex.sum(cplex.prod(1.0, vars[0]), cplex.prod(-3.0, vars[1]), cplex.prod(1.0, vars[2])), 39);
if (cplex.solve()) {
cplex.output().println("Solution status = " + cplex.getStatus());
cplex.output().println("Solution value = " + cplex.getObjValue());
double[] val = cplex.getValues(vars);
int ncols = cplex.getNcols();
for (int j = 0; j < ncols; ++j) {
cplex.output().println("Column: " + j + " Value = " + val[j]);
}
}
cplex.end();
}
相信大家通过注释和自己的理解应该能看出大概求解流程,当然上面的还只是简单案例
案例总结
- Cplex 求解器的构成:
- 决策变量的定义
- 目标函数的形式
- 约束条件的表达
Cplex 的常用 api
变量定义
- IloNumVar(IloIntVar) :模型需要求解的决策变量
eg:创建三个连续(离散)变量,lb 和 ub 是变量取值的上限和下限。
IloNumVar[] x = cplex.numVarArray(3, lb, ub);
- IloNumExpr(IloIntExpr):模型的中间变量。通常是关于决策变量的表达式。
eg: 对于 x 0 + 2 x 1 + 3 x 2 x_0 + 2x_1+ 3x_2 x0+2x1+3x2 ,cplex 中写为:
IloNumExpr expr = cplex.sum(x[0], cplex.prod(2.0, x[1]),cplex.prod(3.0, x[2]))。
- IloRange :模型的变量的约束范围。cplex.addLe()和 cplex.addEq()的返回值。通常在添加约束的时候用到。
eg: 形如 l b < = e x p r < = u b lb <= expr <= ub lb<=expr<=ub,
cplex.addLe(10, x1)
表示的就是 x 1 ⩾ 10 x_1\geqslant10 x1⩾10。cplex.addEq(double arg0,IloNumExpr arg1)
: 返回值为 IloRange,即 a r g 0 ⩽ a r g 1 ⩽ a r g 0 arg_0 \leqslant arg_1 \leqslant arg_0 arg0⩽arg1⩽arg0 。也就是 arg1 的值取值为 arg0,相当于赋了一个常数。
- IloObjective :模型的优化目标。cplex.addMaximize()和 cplex.addMinimize()的返回值。
eg : 最小化(最大化)目标函数。
对于求表达式 x 1 + 2 x 2 + 3 x 3 x_1 + 2x_2 + 3x_3 x1+2x2+3x3 的最小值,cplex 中写为(一种写法):
cplex.addMinimize(cplex.sum(x1,cplex.prob(2,x2),cplex.prob(3,x3)))
另一种写法:double[] objvals = {1.0,2.0,3.0};cplex.addMinimize(cplex.scalProd(x, objvals))
常用方法
-
加法:
cplex.sum();
-
减法:
cplex.diff();
-
乘法:
cplex.prod();
-
除法:
cplex.prod();
计算除法的方法和乘法的一致,也就是将除法转化为乘法。比如 C=A/B 就可以转化为 A=B * C. -
累加求和:
cplex.scalProd(INumExpr[], double[]);
有关括号里的参数类型需要根据具体问题具体分析。
eg:对于 x 1 + 2 x 2 + 3 x 3 + 4 x 4 x_1+2x_2+3x_3+4x_4 x1+2x2+3x3+4x4 ,cplex 中写为:
double[] objvals = {1.0, 2.0, 3.0, 4.0};
IloIntVar[] x = cplex.intVarArray(4, lb1, ub1);
cplex.scalProd(x, objvals);
-
平方:
cplex.square();
-
创建一个 0-1 变量:
cplex.boolVar();
eg:若想定义一个取值为 0 或 1 的变量,cplex 写为:
IloIntVar y = cplex.boolVar();
此时 y 的取值只能是 0 或 1。
- 创建一个数组:
cplex.numVarArray()
,类似的还有,cplex.intVarArray()
,cplex.boolVarArray
,cplex.numExprArray()
等等;
eg:创建一个包含 8 个变量的数组,上限为 lb,下限为 ub,cplex 中写为:
IloNumVar[] x = cplex.numVarArray(8, lb, ub);
约束方程的表达
- 不等式约束:
cplex.addLe();
eg: 对于不等式约束 − x [ 0 ] + x [ 1 ] + x [ 2 ] < = 20.0 -x[0] + x[1] + x[2] <= 20.0 −x[0]+x[1]+x[2]<=20.0,cplex中写为:
cplex.addLe(cplex.sum(cplex.negative(x[0]), x[1], x[2]), 20);
- 等式约束:
cplex.addEq();
eg: 对于等式约束 x 0 = = x 1 x_0==x_1 x0==x1 , 则可以用
cplex.addEq(x0,x1)
。
- cplex能接受的非线性表达式
1、 min和minl:多个数字表达式的最小值;
2、 max和maxl:多个数字表达式的最大值;
3、 abs:数字表达式的绝对值;
- 线性表达式:
cplex.linearNumExpr();
eg: 创建一个线性表达式,求出一数组所有元素的总和。cplex写为:
IloNumVar [][] x = new IloNumVar [][];
x = new IloNumVar[20][30];
for(int i=0; i < 10; i++) {
IloLinearNumExpr summer=cplex.linearNumExpr();//默认为null.
for(int j=0; j < 10; j++) {
summer.addTerm(1.0, x[i][j]);
}
IloRange con = cplex.addEq(summer, 1);
con.setName("yourConstraintName(" + i + ")");
}
数字表达式的分段线性组合
- 分段线性函数的表达:
cplex.pieceiwiseLinear()
eg:表示一个分段线性函数: 当 x ⩽ 100 , 斜率为 1 ; 100 ⩽ x ⩽ 200 ,斜率为 2 ; x > 200 , 斜率为 − 3 当x\leqslant100,斜率为1;100\leqslant x \leqslant200,斜率为2;x>200,斜率为-3 当x⩽100,斜率为1;100⩽x⩽200,斜率为2;x>200,斜率为−3。代码实现:
IloNumVar x = cplex.numVar(-Double.MAX_VALUE, Double.MAX_VALUE);//定义一个实数范围内的x
double [] points = {100,200};//两个分割点
double [] slopes = {1,2,-3};//斜率
IloNumExpr fx = cplex.piecewiseLinear(x1, points, slopes, 0, 300);
求解模型
-
IloCplex.solve()
:精确求解 -
IloCplex.solveRelaxed()
:松弛求解 -
IloCplex.getStatus()
:针对于模型无界或者不可行的情况,可调用该方法得到模型解的状态。
输出格式
cplex.output().println()
:cplex中的打印格式。
eg:
cplex.output().println("Solution status = " + cplex.getStatus());
cplex.output().println("Solution value = " + cplex.getObjValue());
-
cplex.getObjValue()
: 得到目标函数的最值。 -
cplex.getValue()
: 得到最优目标函数值所对应的最优解x,即决策变量的值。 -
cplex.getstatus()
: 返回optimal或者其他情况。
常见异常
java.lang.NullPointerException
:空指针异常
空指针就是空引用,java空指针异常就是引用本身为空,却调用了方法,这个时候就会出现空指针异常。因为我们在定义一个中间变量IloNumExpr的时候,我们没有对其初始化,又因该表达式默认是为null,所以,我们有必要为此类变量指定其存储数据的上下限。否则就会产生空指针异常。通常的做法是创建两个变量,一个作为下限,一个作为上限。指定上限和下限的值,并令上文提到的变量处于上限和下限之间即可。举个例子,以下代码定义了EES_t数组变量的上下限变量lEES_t,uEES_t。然后指定其值分别是0,1000。最后通过cplex.numVarArray()方法使得数组变量EES_t存储数据的取值范围处于0~1000。
int R1,R2 = 10;
double[][] lEES_t = new double[R1][R2];
double[][] uEES_t = new double[R1][R2];
for (int i = 0; i < R1; i++) {
for (int j = 0; j < R2; j++) {
lEES_t[i][j] = 0;
uEES_t[i][j] = 1000;
}
}
for (int i = 0; i < R1; i++) {
EES_t[i] = cplex.numVarArray(R2, lEES_t[i], uEES_t[i]);
}
tips
- 当我们发现编译通过了,但是求解的结果是不可行(不可行指的是不存在满足所有约束、界限和整数性限制的解)或者无界(无界指的是可以使目标函数任意大)的状态。