一、運(yùn)輸問(wèn)題理論介紹
在經(jīng)濟(jì)建設(shè)中扮叨,經(jīng)常碰到大宗物資調(diào)運(yùn)問(wèn)題缤弦。如煤炭、鋼材彻磁、木材碍沐、糧食等物資狸捅,在全國(guó)有若干生產(chǎn)基地,根據(jù)已有的交通網(wǎng)累提,應(yīng)該如何指定調(diào)運(yùn)方案尘喝,將這些物資運(yùn)到各消費(fèi)地點(diǎn),而總運(yùn)費(fèi)要最小斋陪。這個(gè)問(wèn)題可以用以下數(shù)學(xué)語(yǔ)言描述朽褪。
已知有個(gè)生產(chǎn)地點(diǎn)
∥扌椋可供應(yīng)某種物資缔赠,其供應(yīng)量(產(chǎn)量)分別為
,有
個(gè)銷(xiāo)地
骑科,其需要量分別為
橡淑,從
到
運(yùn)輸單位物資的運(yùn)價(jià)(單價(jià))為
,這些數(shù)據(jù)可以匯總于產(chǎn)銷(xiāo)平衡表和單位運(yùn)價(jià)表中咆爽。
產(chǎn)地 | 銷(xiāo)地 | 產(chǎn)量 |
---|---|---|
1 2 … n | ||
1 | ||
2 | ||
m | ||
銷(xiāo)量 |
產(chǎn)銷(xiāo)平衡表
產(chǎn)地 | 銷(xiāo)地 |
---|---|
1 2 … n | |
1 |
|
2 |
|
m |
|
單位運(yùn)價(jià)表
若用 表示從
到
的運(yùn)量梁棠,那么在產(chǎn)銷(xiāo)平衡的條件下,要求得總運(yùn)費(fèi)最小得調(diào)運(yùn)方案斗埂,可求解以下數(shù)學(xué)模型:
這就是運(yùn)輸問(wèn)題得數(shù)學(xué)模型符糊。它包含m×n個(gè)變量,(m+n)個(gè)約束方程呛凶。其系數(shù)矩陣得結(jié)構(gòu)比較松散男娄,且特殊。
該系數(shù)矩陣中對(duì)應(yīng)于變量 的系數(shù)向量
漾稀,其分量中除第
個(gè)和第
個(gè)為1以外模闲,其余的都為零。
對(duì)產(chǎn)銷(xiāo)平衡得運(yùn)輸問(wèn)題崭捍,由于有以下關(guān)系存在
所以模型最多只有個(gè)獨(dú)立約束方程尸折。即稀疏矩陣得秩
。由于有以上特征殷蛇,所以求解運(yùn)輸問(wèn)題時(shí)实夹,可用比較簡(jiǎn)單得計(jì)算方法,習(xí)慣上稱(chēng)為表上作業(yè)法粒梦。
對(duì)產(chǎn)銷(xiāo)平衡得運(yùn)輸問(wèn)題亮航,一定存在可行解。又因?yàn)樗凶兞坑薪缭让牵蚨嬖谧顑?yōu)解缴淋。
二、cplex解決運(yùn)輸問(wèn)題
歸根到底運(yùn)輸問(wèn)題還是一個(gè)線性規(guī)劃問(wèn)題,和cplex入門(mén)系列(二)中的LP問(wèn)題并沒(méi)有什么不同宴猾,但是由于運(yùn)輸問(wèn)題是一個(gè)特殊的線性規(guī)劃問(wèn)題圆存,即其一定存在可行解叼旋,且其結(jié)構(gòu)特殊仇哆,故可以使用表上作業(yè)法進(jìn)行求解。但是在cplex中夫植,我們還是會(huì)使用求解一般線性規(guī)劃的求解方式來(lái)求解問(wèn)題讹剔。
2.1 案例一
假設(shè)有三個(gè)工廠Sunnyvale、Dublin详民、Bangkok供應(yīng)四個(gè)地區(qū)Amarillo延欠、Teaneck、Chicago和Sioux Falls的貨物沈跨。各地區(qū)需求量及從各工廠到各地區(qū)送單位貨物(柜)的運(yùn)價(jià)表如圖所示由捎。試求出總的運(yùn)費(fèi)最節(jié)省的貨物運(yùn)輸方案。
工廠/需求地區(qū) | Amarillo | Teaneck | Chicago | Sioux Falls | 產(chǎn)量(柜) |
---|---|---|---|---|---|
Sunnyvale | 250 | 420 | 380 | 280 | 45 |
Dublin | 1280 | 990 | 1440 | 1520 | 120 |
Bangkok | 1550 | 1420 | 1660 | 1730 | 95 |
需求量(柜) | 80 | 78 | 47 | 55 |
數(shù)學(xué)建模:這是一個(gè)產(chǎn)銷(xiāo)平衡的運(yùn)輸問(wèn)題饿凛,對(duì)應(yīng)數(shù)學(xué)模型為:
用單位運(yùn)價(jià)表和產(chǎn)銷(xiāo)平衡表的數(shù)據(jù)帶入上述數(shù)學(xué)模型
即有
該問(wèn)題是一個(gè)純粹的線性規(guī)劃問(wèn)題狞玛,即可以利潤(rùn)cplex的LP求解器來(lái)求解。
產(chǎn)/銷(xiāo)地類(lèi)定義(TransportationNode )
package com.opreation.research;
/**
* 這個(gè)用于定義運(yùn)輸節(jié)點(diǎn)
*/
public class TransportationNode {
final String NodeName; // 節(jié)點(diǎn)名稱(chēng)
final int quantity; // 產(chǎn)/銷(xiāo)量
final boolean isSource; // true 為產(chǎn)地 false 為銷(xiāo)地
final int id; // 下標(biāo)
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;
}
}
運(yùn)價(jià)/距定義(TransportationRelation )
package com.opreation.research;
/**
* @Description: 定義運(yùn)價(jià)涧窒、運(yùn)距
* @Author
* @Date 2022/8/5 10:01
*/
public class TransportationRelation {
final int [] distance ;
public TransportationRelation(int[] distance) {
this.distance = distance;
}
}
cplex數(shù)據(jù)建模
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: 定義運(yùn)輸模型
* @Author
* @Date 2022/8/5 10:13
*/
public class TransportationModel {
IloCplex cplex ;
double objectiveValue;
IloNumVar x[];
int numOfSource = 0; // 產(chǎn)地?cái)?shù)量
int numOfSink = 0; // 銷(xiāo)地?cái)?shù)量
/**
* 假設(shè)有
*/
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);
}
/**
* 構(gòu)建決策變量
*/
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));
}
}
}
/**
* 構(gòu)建目標(biāo)函數(shù)心肪,最小化運(yùn)輸距離(運(yùn)價(jià))
*/
public void CreatObjFunc(TransportationRelation transportationRelation) throws IloException{
cplex.addMinimize(cplex.scalProd(x,transportationRelation.distance));
}
/**
* 構(gòu)建約束條件
*/
public void CreatConstraints(List<TransportationNode> transportationNodeList) throws IloException {
for(TransportationNode tsNode : transportationNodeList){
IloNumExpr left = cplex.numExpr();
if (tsNode.isSource()) {
// 產(chǎn)地節(jié)點(diǎn),約束條件為纠吴,流出產(chǎn)地的供應(yīng)量和產(chǎn)地決策變量相等
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 {
// 銷(xiāo)地節(jié)點(diǎn)硬鞍,約束條件為,流入銷(xiāo)地的供應(yīng)量與銷(xiāo)地決策變量相等
for(int i = 0;i<numOfSource;i++) {
left = cplex.sum(left,cplex.prod(1,x[tsNode.id+i*numOfSink]));
}
cplex.addEq(left,tsNode.quantity);
}
}
}
/**
* 線性規(guī)劃模型求解問(wèn)題
*/
public void Solve(List<TransportationNode> transportationNodeList) throws IloException {
cplex.setOut(null);
if (cplex.solve()) {
cplex.exportModel("transportationModel.lp");
objectiveValue = cplex.getObjValue();
System.out.println("最小運(yùn)費(fèi)為:" + 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("無(wú)解");
}
}
}
主函數(shù)(傳入?yún)?shù)調(diào)用求解器)
package com.opreation.research;
import ilog.concert.IloException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* @Description: 傳入?yún)?shù)戴已,求解模型
* @Author
* @Date 2022/8/5 10:56
*/
public class Main {
public static void main(String[] args) throws IOException, IloException {
// 產(chǎn)銷(xiāo)數(shù)據(jù)節(jié)點(diǎn)
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);
// 產(chǎn)銷(xiāo)地運(yùn)價(jià)/運(yùn)距
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);
}
}
運(yùn)行結(jié)果
最小運(yùn)費(fèi)為: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
打開(kāi)transportationModel.lp可以看到模型結(jié)構(gòu)固该,和我們構(gòu)建的數(shù)學(xué)模型一致:
\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
該案例為《運(yùn)籌學(xué)》清華大學(xué)第四版P107 例4-1題目,具體應(yīng)用場(chǎng)景請(qǐng)參考教材糖儡。
某公司經(jīng)銷(xiāo)售甲產(chǎn)品伐坏。它下設(shè)三個(gè)加工廠。每日的產(chǎn)量分別是: 為7噸休玩,
為4噸著淆,
為9噸。該公司把這些產(chǎn)品分別運(yùn)往四個(gè)銷(xiāo)售點(diǎn)拴疤。各銷(xiāo)售點(diǎn)每日銷(xiāo)量為:
為3噸永部,
為6噸,
為5噸呐矾,
為6噸苔埋。已知從各工廠到各銷(xiāo)售點(diǎn)的單位產(chǎn)品的運(yùn)價(jià)如下表所示。問(wèn)該公司應(yīng)該如何調(diào)運(yùn)產(chǎn)品蜒犯,在滿(mǎn)足各銷(xiāo)點(diǎn)的需求量的前提下组橄,使總運(yùn)費(fèi)為最少荞膘。
加工廠/銷(xiāo)地 | ||||
---|---|---|---|---|
3 | 11 | 3 | 10 | |
1 | 9 | 2 | 8 | |
7 | 4 | 10 | 5 |
加工廠/銷(xiāo)地 | 產(chǎn)量 | ||||
---|---|---|---|---|---|
7 | |||||
4 | |||||
9 | |||||
銷(xiāo)量 | 3 | 6 | 5 | 6 |
數(shù)學(xué)建模:這是一個(gè)產(chǎn)銷(xiāo)平衡的運(yùn)輸問(wèn)題,對(duì)應(yīng)數(shù)學(xué)模型為:
用單位運(yùn)價(jià)表和產(chǎn)銷(xiāo)平衡表的數(shù)據(jù)帶入上述數(shù)學(xué)模型
即有
該問(wèn)題是一個(gè)純粹的線性規(guī)劃問(wèn)題玉工,即可以利用cplex的LP求解器來(lái)求解羽资。
產(chǎn)/銷(xiāo)地類(lèi)定義(TransportationNode )
package com.opreation.research;
/**
* 這個(gè)用于定義運(yùn)輸節(jié)點(diǎn)
*/
public class TransportationNode {
final String NodeName; // 節(jié)點(diǎn)名稱(chēng)
final int quantity; // 產(chǎn)/銷(xiāo)量
final boolean isSource; // true 為產(chǎn)地 false 為銷(xiāo)地
final int id; // 下標(biāo)
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;
}
}
運(yùn)價(jià)/距定義(TransportationRelation )
package com.opreation.research;
/**
* @Description: 定義運(yùn)價(jià)、運(yùn)距
* @Author
* @Date 2022/8/5 10:01
*/
public class TransportationRelation {
final int [] distance ;
public TransportationRelation(int[] distance) {
this.distance = distance;
}
}
cplex數(shù)據(jù)建模
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: 定義運(yùn)輸模型
* @Author
* @Date 2022/8/5 10:13
*/
public class TransportationModel {
IloCplex cplex ;
double objectiveValue;
IloNumVar x[];
int numOfSource = 0; // 產(chǎn)地?cái)?shù)量
int numOfSink = 0; // 銷(xiāo)地?cái)?shù)量
/**
* 假設(shè)有
*/
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);
}
/**
* 構(gòu)建決策變量
*/
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));
}
}
}
/**
* 構(gòu)建目標(biāo)函數(shù)遵班,最小化運(yùn)輸距離(運(yùn)價(jià))
*/
public void CreatObjFunc(TransportationRelation transportationRelation) throws IloException{
cplex.addMinimize(cplex.scalProd(x,transportationRelation.distance));
}
/**
* 構(gòu)建約束條件
*/
public void CreatConstraints(List<TransportationNode> transportationNodeList) throws IloException {
for(TransportationNode tsNode : transportationNodeList){
IloNumExpr left = cplex.numExpr();
if (tsNode.isSource()) {
// 產(chǎn)地節(jié)點(diǎn)屠升,約束條件為,流出產(chǎn)地的供應(yīng)量和產(chǎn)地決策變量相等
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 {
// 銷(xiāo)地節(jié)點(diǎn)狭郑,約束條件為腹暖,流入銷(xiāo)地的供應(yīng)量與銷(xiāo)地決策變量相等
for(int i = 0;i<numOfSource;i++) {
left = cplex.sum(left,cplex.prod(1,x[tsNode.id+i*numOfSink]));
}
cplex.addEq(left,tsNode.quantity);
}
}
}
/**
* 線性規(guī)劃模型求解問(wèn)題
*/
public void Solve(List<TransportationNode> transportationNodeList) throws IloException {
cplex.setOut(null);
if (cplex.solve()) {
cplex.exportModel("transportationModel.lp");
objectiveValue = cplex.getObjValue();
System.out.println("最小運(yùn)費(fèi)為:" + 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("無(wú)解");
}
}
}
主函數(shù)(傳入?yún)?shù)調(diào)用求解器)
package com.opreation.research;
import ilog.concert.IloException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* @Description: 傳入?yún)?shù),求解模型
* @Author
* @Date 2022/8/5 10:56
*/
public class Main {
public static void main(String[] args) throws IOException, IloException {
// 產(chǎn)銷(xiāo)數(shù)據(jù)節(jié)點(diǎn)
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);
// 產(chǎn)銷(xiāo)地運(yùn)價(jià)/運(yùn)距
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);
}
}
運(yùn)行結(jié)果
最小運(yùn)費(fèi)為: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
打開(kāi)transportationModel.lp可以看到模型結(jié)構(gòu)翰萨,和我們構(gòu)建的數(shù)學(xué)模型一致:
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
可以看到結(jié)果和書(shū)上例題給出的最優(yōu)解結(jié)果一致脏答。