一、运输问题理论介绍
在经济建设中,经常碰到大宗物资调运问题。如煤炭、钢材、木材、粮食等物资,在全国有若干生产基地,根据已有的交通网,应该如何指定调运方案,将这些物资运到各消费地点,而总运费要最小。这个问题可以用以下数学语言描述。
已知有个生产地点。可供应某种物资,其供应量(产量)分别为,有个销地,其需要量分别为,从到运输单位物资的运价(单价)为,这些数据可以汇总于产销平衡表和单位运价表中。
产地 | 销地 | 产量 |
---|---|---|
1 2 … n | ||
1 | ||
2 | ||
m | ||
销量 |
产销平衡表
产地 | 销地 |
---|---|
1 2 … n | |
1 | |
2 | |
m |
单位运价表
若用 表示从 到 的运量,那么在产销平衡的条件下,要求得总运费最小得调运方案,可求解以下数学模型:
这就是运输问题得数学模型。它包含m×n个变量,(m+n)个约束方程。其系数矩阵得结构比较松散,且特殊。
该系数矩阵中对应于变量 的系数向量,其分量中除第个和第个为1以外,其余的都为零。
对产销平衡得运输问题,由于有以下关系存在
所以模型最多只有个独立约束方程。即稀疏矩阵得秩。由于有以上特征,所以求解运输问题时,可用比较简单得计算方法,习惯上称为表上作业法。
对产销平衡得运输问题,一定存在可行解。又因为所有变量有界,因而存在最优解。
二、cplex解决运输问题
归根到底运输问题还是一个线性规划问题,和cplex入门系列(二)中的LP问题并没有什么不同,但是由于运输问题是一个特殊的线性规划问题,即其一定存在可行解,且其结构特殊,故可以使用表上作业法进行求解。但是在cplex中,我们还是会使用求解一般线性规划的求解方式来求解问题。
2.1 案例一
假设有三个工厂Sunnyvale、Dublin、Bangkok供应四个地区Amarillo、Teaneck、Chicago和Sioux Falls的货物。各地区需求量及从各工厂到各地区送单位货物(柜)的运价表如图所示。试求出总的运费最节省的货物运输方案。
工厂/需求地区 | Amarillo | Teaneck | Chicago | Sioux Falls | 产量(柜) |
---|---|---|---|---|---|
Sunnyvale | 250 | 420 | 380 | 280 | 45 |
Dublin | 1280 | 990 | 1440 | 1520 | 120 |
Bangkok | 1550 | 1420 | 1660 | 1730 | 95 |
需求量(柜) | 80 | 78 | 47 | 55 |
数学建模:这是一个产销平衡的运输问题,对应数学模型为:
用单位运价表和产销平衡表的数据带入上述数学模型
即有
该问题是一个纯粹的线性规划问题,即可以利润cplex的LP求解器来求解。
产/销地类定义(TransportationNode )
package com.opreation.research;
/**
* 这个用于定义运输节点
*/
public class TransportationNode {
final String NodeName; // 节点名称
final int quantity; // 产/销量
final boolean isSource; // true 为产地 false 为销地
final int id; // 下标
public TransportationNode(String NodeName,int quantity,boolean isSource,int id) {
this.NodeName = NodeName;
this.quantity = quantity;
this.isSource = isSource;
this.id = id;
}
public boolean isSource() {
return this.isSource;
}
}
运价/距定义(TransportationRelation )
package com.opreation.research;
/**
* @Description: 定义运价、运距
* @Author
* @Date 2022/8/5 10:01
*/
public class TransportationRelation {
final int [] distance ;
public TransportationRelation(int[] distance) {
this.distance = distance;
}
}
cplex数据建模
package com.opreation.research;
import ilog.concert.IloException;
import ilog.concert.IloNumExpr;
import ilog.concert.IloNumVar;
import ilog.cplex.IloCplex;
import java.util.List;
/**
* @Description: 定义运输模型
* @Author
* @Date 2022/8/5 10:13
*/
public class TransportationModel {
IloCplex cplex ;
double objectiveValue;
IloNumVar x[];
int numOfSource = 0; // 产地数量
int numOfSink = 0; // 销地数量
/**
* 假设有
*/
public void bulidModel(List<TransportationNode> transportationNodeList, TransportationRelation transportationRelation)throws IloException {
this.cplex = new IloCplex();
for(TransportationNode tsNode : transportationNodeList){
if (tsNode.isSource()) {
numOfSource++;
}else {
numOfSink++;
}
}
System.out.println(numOfSource);
System.out.println(numOfSink);
CreatDecisionVariable();
CreatObjFunc(transportationRelation);
CreatConstraints(transportationNodeList);
Solve(transportationNodeList);
}
/**
* 构建决策变量
*/
public void CreatDecisionVariable() throws IloException{
x = new IloNumVar[numOfSource*numOfSink];
for (int i = 0; i < numOfSource; i++) {
for (int j = 0; j < numOfSink; j++) {
x[i*numOfSink+j] = cplex.numVar(0, Double.MAX_VALUE,"x"+(i+1)+(j+1));
int a = i*numOfSink+j;
System.out.println("x"+a+":" + "x"+(i+1)+(j+1));
}
}
}
/**
* 构建目标函数,最小化运输距离(运价)
*/
public void CreatObjFunc(TransportationRelation transportationRelation) throws IloException{
cplex.addMinimize(cplex.scalProd(x,transportationRelation.distance));
}
/**
* 构建约束条件
*/
public void CreatConstraints(List<TransportationNode> transportationNodeList) throws IloException {
for(TransportationNode tsNode : transportationNodeList){
IloNumExpr left = cplex.numExpr();
if (tsNode.isSource()) {
// 产地节点,约束条件为,流出产地的供应量和产地决策变量相等
for(int i = 0;i<numOfSink;i++) {
left = cplex.sum(left,cplex.prod(1,x[tsNode.id*numOfSink+i]));
}
cplex.addEq(left,tsNode.quantity);
}else {
// 销地节点,约束条件为,流入销地的供应量与销地决策变量相等
for(int i = 0;i<numOfSource;i++) {
left = cplex.sum(left,cplex.prod(1,x[tsNode.id+i*numOfSink]));
}
cplex.addEq(left,tsNode.quantity);
}
}
}
/**
* 线性规划模型求解问题
*/
public void Solve(List<TransportationNode> transportationNodeList) throws IloException {
cplex.setOut(null);
if (cplex.solve()) {
cplex.exportModel("transportationModel.lp");
objectiveValue = cplex.getObjValue();
System.out.println("最小运费为:" + objectiveValue);
for (int i = 0; i < numOfSource; i++) {
for (int j = 0; j < numOfSink; j++) {
System.out.print(transportationNodeList.get(i).NodeName+" to "+transportationNodeList.get(numOfSource+j).NodeName+" : ");
System.out.println(cplex.getValue(x[i*numOfSink+j]));
}
}
}else{
System.out.println("无解");
}
}
}
主函数(传入参数调用求解器)
package com.opreation.research;
import ilog.concert.IloException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* @Description: 传入参数,求解模型
* @Author
* @Date 2022/8/5 10:56
*/
public class Main {
public static void main(String[] args) throws IOException, IloException {
// 产销数据节点
TransportationNode transportationNode1 = new TransportationNode("Sunnyvale", 45, true, 0);
TransportationNode transportationNode2 = new TransportationNode("Dublin", 120, true, 1);
TransportationNode transportationNode3 = new TransportationNode("Bangkok", 95, true, 2);
TransportationNode transportationNode4 = new TransportationNode("Amarillo", 80, false, 0);
TransportationNode transportationNode5 = new TransportationNode("Teaneck", 78, false, 1);
TransportationNode transportationNode6 = new TransportationNode("Chicago", 47, false, 2);
TransportationNode transportationNode7 = new TransportationNode("Sioux Falls", 55, false, 3);
List<TransportationNode> transportationNodeList = new ArrayList<TransportationNode>();
transportationNodeList.add(transportationNode1);
transportationNodeList.add(transportationNode2);
transportationNodeList.add(transportationNode3);
transportationNodeList.add(transportationNode4);
transportationNodeList.add(transportationNode5);
transportationNodeList.add(transportationNode6);
transportationNodeList.add(transportationNode7);
// 产销地运价/运距
int [] dis = new int[]{250, 420, 380, 280, 1280, 990, 1440, 1520, 1550, 1420, 1660, 1730};
TransportationRelation transportationRelation = new TransportationRelation(dis);
TransportationModel model = new TransportationModel();
model.bulidModel(transportationNodeList, transportationRelation);
}
}
运行结果
最小运费为:297800.0
Sunnyvale to Amarillo : 0.0
Sunnyvale to Teaneck : 0.0
Sunnyvale to Chicago : 0.0
Sunnyvale to Sioux Falls : 45.0
Dublin to Amarillo : 42.0
Dublin to Teaneck : 78.0
Dublin to Chicago : 0.0
Dublin to Sioux Falls : 0.0
Bangkok to Amarillo : 38.0
Bangkok to Teaneck : 0.0
Bangkok to Chicago : 47.0
Bangkok to Sioux Falls : 10.0
打开transportationModel.lp可以看到模型结构,和我们构建的数学模型一致:
\ENCODING=ISO-8859-1
\Problem name: ilog.cplex
Minimize
obj: 250 x11 + 420 x12 + 380 x13 + 280 x14 + 1280 x21 + 990 x22 + 1440 x23
+ 1520 x24 + 1550 x31 + 1420 x32 + 1660 x33 + 1730 x34
Subject To
c1: x11 + x12 + x13 + x14 = 45
c2: x21 + x22 + x23 + x24 = 120
c3: x31 + x32 + x33 + x34 = 95
c4: x11 + x21 + x31 = 80
c5: x12 + x22 + x32 = 78
c6: x13 + x23 + x33 = 47
c7: x14 + x24 + x34 = 55
End
2.2 案例2
该案例为《运筹学》清华大学第四版P107 例4-1题目,具体应用场景请参考教材。
某公司经销售甲产品。它下设三个加工厂。每日的产量分别是: 为7吨, 为4吨, 为9吨。该公司把这些产品分别运往四个销售点。各销售点每日销量为: 为3吨, 为6吨, 为5吨, 为6吨。已知从各工厂到各销售点的单位产品的运价如下表所示。问该公司应该如何调运产品,在满足各销点的需求量的前提下,使总运费为最少。
加工厂/销地 | ||||
---|---|---|---|---|
3 | 11 | 3 | 10 | |
1 | 9 | 2 | 8 | |
7 | 4 | 10 | 5 |
加工厂/销地 | 产量 | ||||
---|---|---|---|---|---|
7 | |||||
4 | |||||
9 | |||||
销量 | 3 | 6 | 5 | 6 |
数学建模:这是一个产销平衡的运输问题,对应数学模型为:
用单位运价表和产销平衡表的数据带入上述数学模型
即有
该问题是一个纯粹的线性规划问题,即可以利用cplex的LP求解器来求解。
产/销地类定义(TransportationNode )
package com.opreation.research;
/**
* 这个用于定义运输节点
*/
public class TransportationNode {
final String NodeName; // 节点名称
final int quantity; // 产/销量
final boolean isSource; // true 为产地 false 为销地
final int id; // 下标
public TransportationNode(String NodeName,int quantity,boolean isSource,int id) {
this.NodeName = NodeName;
this.quantity = quantity;
this.isSource = isSource;
this.id = id;
}
public boolean isSource() {
return this.isSource;
}
}
运价/距定义(TransportationRelation )
package com.opreation.research;
/**
* @Description: 定义运价、运距
* @Author
* @Date 2022/8/5 10:01
*/
public class TransportationRelation {
final int [] distance ;
public TransportationRelation(int[] distance) {
this.distance = distance;
}
}
cplex数据建模
package com.opreation.research;
import ilog.concert.IloException;
import ilog.concert.IloNumExpr;
import ilog.concert.IloNumVar;
import ilog.cplex.IloCplex;
import java.util.List;
/**
* @Description: 定义运输模型
* @Author
* @Date 2022/8/5 10:13
*/
public class TransportationModel {
IloCplex cplex ;
double objectiveValue;
IloNumVar x[];
int numOfSource = 0; // 产地数量
int numOfSink = 0; // 销地数量
/**
* 假设有
*/
public void bulidModel(List<TransportationNode> transportationNodeList, TransportationRelation transportationRelation)throws IloException {
this.cplex = new IloCplex();
for(TransportationNode tsNode : transportationNodeList){
if (tsNode.isSource()) {
numOfSource++;
}else {
numOfSink++;
}
}
System.out.println(numOfSource);
System.out.println(numOfSink);
CreatDecisionVariable();
CreatObjFunc(transportationRelation);
CreatConstraints(transportationNodeList);
Solve(transportationNodeList);
}
/**
* 构建决策变量
*/
public void CreatDecisionVariable() throws IloException{
x = new IloNumVar[numOfSource*numOfSink];
for (int i = 0; i < numOfSource; i++) {
for (int j = 0; j < numOfSink; j++) {
x[i*numOfSink+j] = cplex.numVar(0, Double.MAX_VALUE,"x"+(i+1)+(j+1));
int a = i*numOfSink+j;
System.out.println("x"+a+":" + "x"+(i+1)+(j+1));
}
}
}
/**
* 构建目标函数,最小化运输距离(运价)
*/
public void CreatObjFunc(TransportationRelation transportationRelation) throws IloException{
cplex.addMinimize(cplex.scalProd(x,transportationRelation.distance));
}
/**
* 构建约束条件
*/
public void CreatConstraints(List<TransportationNode> transportationNodeList) throws IloException {
for(TransportationNode tsNode : transportationNodeList){
IloNumExpr left = cplex.numExpr();
if (tsNode.isSource()) {
// 产地节点,约束条件为,流出产地的供应量和产地决策变量相等
for(int i = 0;i<numOfSink;i++) {
left = cplex.sum(left,cplex.prod(1,x[tsNode.id*numOfSink+i]));
}
cplex.addEq(left,tsNode.quantity);
}else {
// 销地节点,约束条件为,流入销地的供应量与销地决策变量相等
for(int i = 0;i<numOfSource;i++) {
left = cplex.sum(left,cplex.prod(1,x[tsNode.id+i*numOfSink]));
}
cplex.addEq(left,tsNode.quantity);
}
}
}
/**
* 线性规划模型求解问题
*/
public void Solve(List<TransportationNode> transportationNodeList) throws IloException {
cplex.setOut(null);
if (cplex.solve()) {
cplex.exportModel("transportationModel.lp");
objectiveValue = cplex.getObjValue();
System.out.println("最小运费为:" + objectiveValue);
for (int i = 0; i < numOfSource; i++) {
for (int j = 0; j < numOfSink; j++) {
System.out.print(transportationNodeList.get(i).NodeName+" to "+transportationNodeList.get(numOfSource+j).NodeName+" : ");
System.out.println(cplex.getValue(x[i*numOfSink+j]));
}
}
}else{
System.out.println("无解");
}
}
}
主函数(传入参数调用求解器)
package com.opreation.research;
import ilog.concert.IloException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* @Description: 传入参数,求解模型
* @Author
* @Date 2022/8/5 10:56
*/
public class Main {
public static void main(String[] args) throws IOException, IloException {
// 产销数据节点
TransportationNode transportationNode1 = new TransportationNode("A1", 7, true, 0);
TransportationNode transportationNode2 = new TransportationNode("A2", 4, true, 1);
TransportationNode transportationNode3 = new TransportationNode("A3", 9, true, 2);
TransportationNode transportationNode4 = new TransportationNode("B1", 3, false, 0);
TransportationNode transportationNode5 = new TransportationNode("B2", 6, false, 1);
TransportationNode transportationNode6 = new TransportationNode("B3", 5, false, 2);
TransportationNode transportationNode7 = new TransportationNode("B4", 6, false, 3);
List<TransportationNode> transportationNodeList = new ArrayList<TransportationNode>();
transportationNodeList.add(transportationNode1);
transportationNodeList.add(transportationNode2);
transportationNodeList.add(transportationNode3);
transportationNodeList.add(transportationNode4);
transportationNodeList.add(transportationNode5);
transportationNodeList.add(transportationNode6);
transportationNodeList.add(transportationNode7);
// 产销地运价/运距
int [] dis = new int[]{3, 11, 3, 10, 1, 9, 2, 8, 7, 4, 10, 5};
TransportationRelation transportationRelation = new TransportationRelation(dis);
TransportationModel model = new TransportationModel();
model.bulidModel(transportationNodeList, transportationRelation);
}
}
运行结果
最小运费为:85.0
A1 to B1 : 0.0
A1 to B2 : 0.0
A1 to B3 : 5.0
A1 to B4 : 2.0
A2 to B1 : 3.0
A2 to B2 : 0.0
A2 to B3 : 0.0
A2 to B4 : 1.0
A3 to B1 : 0.0
A3 to B2 : 6.0
A3 to B3 : 0.0
A3 to B4 : 3.0
打开transportationModel.lp可以看到模型结构,和我们构建的数学模型一致:
obj: 3 x11 + 11 x12 + 3 x13 + 10 x14 + x21 + 9 x22 + 2 x23 + 8 x24 + 7 x31
+ 4 x32 + 10 x33 + 5 x34
Subject To
c1: x11 + x12 + x13 + x14 = 7
c2: x21 + x22 + x23 + x24 = 4
c3: x31 + x32 + x33 + x34 = 9
c4: x11 + x21 + x31 = 3
c5: x12 + x22 + x32 = 6
c6: x13 + x23 + x33 = 5
c7: x14 + x24 + x34 = 6
End
可以看到结果和书上例题给出的最优解结果一致。