如何使用Java CPLEX求解p-median问题?请分析该实现方案可行性
P-Median问题的Java实现可行性分析
以下是与CPLEX Studio/opl/examples/opl/pmedian中自带示例等价的OPL模型的Java实现:
import ilog.concert.IloConstraint; import ilog.concert.IloException; import ilog.concert.IloLinearNumExpr; import ilog.concert.IloNumVar; import ilog.concert.IloRange; import ilog.cplex.IloCplex; public class P_Median { public static void main(String args[]) { solveMe(); } public static void solveMe() { int p = 2; int n = 4; // customers int m = 3; // warehouses String[] customers = { "Albert", "Bob", "Chris", "Daniel" }; String[] warehouses = { "Santa Clara", "San Jose", "Berkeley" }; double[] demand = { 100.0, 80.0, 80.0, 70.0 }; double[][] distance = { { 2.0, 10.0, 50.0 }, { 2.0, 10.0, 52.0 }, { 50.0, 60.0, 3.0 }, { 40.0, 60.0, 1.0 } }; double[][]cost = new double [n][m]; for (int c = 0; c < n; c++) { for (int w = 0; w < m; w++) { cost[c][w] = demand[c]*distance[c][w]; } } System.out.println("cost = ["); for (int c = 0; c < n; c++) { for (int w = 0; w < m; w++) { System.out.print(cost[c][w] + "\t"); } System.out.print("\n\r"); } System.out.println("]"); IloCplex cplex = null; try { // define new model cplex = new IloCplex(); //cplex.setParam(IloCplex.Param.Simplex.Display, 0); //variables IloNumVar[] openWarehouse = new IloNumVar[m]; for (int w = 0 ; w < m; w++) { openWarehouse[w] = cplex.boolVar("openWarehouse"+"_"+w); } IloNumVar[][] shipToCustomer = new IloNumVar[n][m]; for (int c = 0; c < n; c++) { for (int w = 0; w < m; w++) { shipToCustomer[c][w] = cplex.boolVar("shipToCustomer"+"_"+c+"_"+w); } } //objective IloLinearNumExpr objective = cplex.linearNumExpr(); for (int c = 0; c < n; c++) { for (int w = 0; w < m; w++) { objective.addTerm(cost[c][w],shipToCustomer[c][w]); } } cplex.addMinimize(objective); // constraints IloConstraint[] shipConstraints = new IloConstraint[n]; IloLinearNumExpr[] shipConstraintExpression = new IloLinearNumExpr[n]; for (int c=0; c<n; c++) { shipConstraintExpression[c] = cplex.linearNumExpr(); for (int w=0; w<m; w++) { shipConstraintExpression[c].addTerm(1.0, shipToCustomer[c][w]); } IloRange shipConstraint = cplex.addEq(shipConstraintExpression[c], 1); shipConstraint.setName("shipConstraint"+c); shipConstraints[c]=shipConstraint; } IloLinearNumExpr openConstraintExpression = cplex.linearNumExpr(); for (int w=0; w<m; w++) { openConstraintExpression.addTerm(1.0, openWarehouse[w]); } IloRange openConstraint = cplex.addEq(openConstraintExpression, p); openConstraint.setName("openConstraint"); IloConstraint[][] shipOpenConstraints = new IloConstraint[n][m]; IloLinearNumExpr[][] shipOpenExpression = new IloLinearNumExpr[n][m]; for (int c = 0; c < n; c++) { for (int w = 0; w < m; w++) { shipOpenExpression[c][w] = cplex.linearNumExpr(); shipOpenExpression[c][w].addTerm(1.0, shipToCustomer[c][w]); IloConstraint shipOpenConstraint = cplex.addLe(shipOpenExpression[c][w], openWarehouse[w]); shipOpenConstraint.setName("shipOpenConstraint"+c+w); shipOpenConstraints[c][w]=shipOpenConstraint; } } // solve model if (cplex.solve()) { System.out.println("\nSolution status = "+ cplex.getStatus()+"\n"); System.out.println("obj = "+cplex.getObjValue()); System.out.println("openWarehouse = ["); for (int w = 0 ; w < m; w++) { System.out.print(cplex.getValue(openWarehouse[w]) + "\t"); } System.out.println("]"); System.out.println("shipToCustomer = ["); for (int c = 0; c < n; c++) { for (int w = 0; w < m; w++) { System.out.print(cplex.getValue(shipToCustomer[c][w]) + "\t"); } System.out.print("\n\r"); } System.out.println("]"); System.out.println("------ ship Constraints -----"); for (int c=0;c<shipConstraints.length;c++) { System.out.print(((IloRange) shipConstraints[c])+" -> "+"\t"); System.out.print("slack constraint "+(c+1)+" = "+cplex.getSlack((IloRange) shipConstraints[c])+"\n"); } System.out.println("------ open Constraint -----"); System.out.print(((IloRange) openConstraint)+" -> "+"\t"); System.out.print("slack constraint = "+cplex.getSlack((IloRange) openConstraint)+"\n"); System.out.println("------ ship Open Constraints -----"); for (int c = 0; c < n; c++) { for (int w = 0; w < m; w++) { System.out.print(((IloRange) shipOpenConstraints[c][w])+" -> "+"\t"); System.out.print("slack constraint "+(c+1)+""+(w+1)+" = "+cplex.getSlack((IloRange) shipOpenConstraints[c][w])+"\n"); } } } else { System.out.println("problem not solved"); } } catch (IloException exc) { exc.printStackTrace(); } finally { if(cplex!=null) cplex.end(); } } }
问题:请分析该解决方案是否可行?
整数规划相关背景
整数规划是一类数学优化或可行性规划问题,其中部分或全部变量被限制为整数。多数场景下,该术语特指整数线性规划(ILP)——目标函数和除整数约束外的其他约束均为线性的规划问题。
整数规划属于NP完全问题,尤其是0-1整数线性规划这一特例:其中变量为二进制且只需满足约束条件,它是卡普提出的21个NP完全问题之一。若部分决策变量为非离散型,则这类问题被称为混合整数规划问题。
可行性分析结论
该Java解决方案完全可行,具体依据如下:
- 模型逻辑准确性
- 完整复现了P-Median问题的核心需求:
- 用二进制变量
openWarehouse标记仓库是否启用,shipToCustomer标记是否从某仓库向某客户供货 - 目标函数正确定义为最小化总运输成本(需求与距离的乘积之和)
- 约束条件覆盖了P-Median问题的核心规则:
- 每个客户必须被恰好一个仓库覆盖(
shipConstraints约束) - 恰好启用指定数量的仓库(
openConstraint约束) - 仅能从已启用的仓库向客户供货(
shipOpenConstraints约束)
- 每个客户必须被恰好一个仓库覆盖(
- 用二进制变量
- 完整复现了P-Median问题的核心需求:
- CPLEX API使用合规性
- 正确初始化
IloCplex求解器实例,并在finally块中调用end()释放资源,避免内存泄漏 - 变量定义、目标函数构建、约束添加的流程完全符合CPLEX Java API的标准用法
- 异常处理逻辑覆盖了
IloException,可捕获求解过程中的异常并输出详细信息
- 正确初始化
- 问题适配性
- 针对P-Median这类0-1整数线性规划问题,使用
boolVar()定义二进制变量,完全匹配问题类型,CPLEX的整数规划求解器可高效处理该规模的实例
- 针对P-Median这类0-1整数线性规划问题,使用
可优化方向
虽然实现可行,但可以做以下小改进提升代码质量:
- 将硬编码的参数(如
p、客户/仓库数量、需求、距离矩阵等)提取为配置项或通过外部文件读取,增强代码灵活性 - 用日志框架替代
System.out打印,便于生产环境的调试与监控 - 针对大规模P-Median问题,可开启CPLEX的并行求解参数或调整分支定界策略,提升求解效率
内容的提问来源于stack exchange,提问作者filipemb
相关产品推荐
相关产品推荐

