基于java语言,调用cplex求解线性/整数规划问题

目录

引言

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​+3x3​s.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∑4​j=0∑4​(ai,j​xi,j​+bi,j​yi,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∑4​xi,j​=1,j=0∑4​yi,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前打个断点,可以很容易地看到具体的优化模型,下图给出了目标函数,以及部分的约束条件。
基于java语言,调用cplex求解线性/整数规划问题
(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()
上一篇:2021-05-03


下一篇:线性最优解java实现+Cplex java调用