运输问题和指派问题—R实现
阅读原文时间:2023年07月08日阅读:1

运输问题经常出现在计划货物配送和从某些供给地区到达需求地区之间的服务中,特别是每个供给地区(起点)的货物可获得量是有限的,每个需求地区(目的地)的货物需求量是已知的。运输问题中最常用的目标是要使货物从起点到目的地的运输成本最低。指派问题将工作分配给机器,对代理分配任务,将销售人员分配给销售区域,将合同分配给投标人,等等。

1. 运输问题

运输问题(transportation problem) 属于线性规划问题,可以根据模型按照线性规划的方式求解,但由于其特殊性,用常规的线性规划来求解并不是最有效的方法。lpSolve包提供了函数lp.transport() 来求解运输问题。

1.1运输问题的数学模型

一般地,产销平衡的运输问题可以表述为:设有\(m\)个地点(称为产地或发地)\(A_1\),\(A_2\),…,\(A_m\)的某种物资调至\(n\)个地点(称为销地或收地)\(B_1\),\(B_2\),…,\(B_n\),各个产地需要调出的物资量分别为\(a_i\)单位,各个销地需要调进的物资量分别为\(b_j\) 单位,且各个发点的供应量之和等于各个收点的需求量之和。已知每个发点\(A_i\) 到每个收点\(B_j\) 的物资单位调运价格为\(c_{ij}\),现问如何安排调运,才能使总运费最小。

于是得产销平衡运输问题的数学模型:

\(mix\) \(z\) = \(\sum_{i=1}^m\)\(\sum_{j=1}^n\)\(c_{ij}\)\(x_{ij}\)

\(\sum_{j=1}^n\)\(x_{ij}\)=\(a_i\) \(i=1,2,…,m\)

\(\sum_{i=1}^m\)\(x_{ij}\)=\(b_j\) \(j=1,2,…,n\)

\(x_{ij} \geq 0\)   \(i,j=1,2,…,n\)

不平衡运输问题只要简单地调整约束的松紧关系即得。

1.2 R计算程序

例1: 有三个造纸厂A1、A2 和A3,造纸量分别为16 个单位、10 个单位和22 个单位,四个客户B1、B2、B3 和B4 的需求量分别为8 个单位、14 个单位、12 个单位和14 个单位。造纸厂到客户之间的单位运价如表所示,确定总运费最少的调运方案。单位运价表如下所示:

     [,1] [,2] [,3] [,4]
[1,]    4   12    4   11
[2,]    2   10    3    9
[3,]    8    5   11    6

总产量等于总销量,都为48 个单位,这是一个产销平衡的运输问题。

R代码及运行结果如下:

library(lpSolve)
costs <-matrix(c(4,2,8,12,10,5,4,3,11,11,9,6),nrow=3)    #运费矩阵
row.signs <- rep("=", 3)                                 #各家造纸厂的产量恰好可以售完,故都取等号
row.rhs <- c(16,10,22)                                   #销量约束值
col.signs <- rep("=", 4)                                 #各家客户需求量恰好可以满足,故都取等号
col.rhs <- c(8,14,12,14)                                 #需求约束值
res <-lp.transport(costs,"min",row.signs,row.rhs,col.signs,col.rhs)

1.3 计算结果

lp.transport(costs,"min",row.signs,row.rhs,col.signs,col.rhs)
Success: the objective function is 244
res <-lp.transport(costs,"min",row.signs,row.rhs,col.signs,col.rhs)
res               #输出最小运费
res$solution      #输出运输方案
     [,1] [,2] [,3] [,4]
[1,]    4    0   12    0
[2,]    4    0    0    6
[3,]    0   14    0    8

输出结果表示问题成功解决,最少运费为244。运送方案为: A1→B1 : 4 个单位,A1→B3 : 12 个单位,A2→B1 :4 个单位,A2→B4 : 6 个单位,A3→B2 : 14 个单位,A3→B4 : 8个单位。

2. 指派问题

指派问题(assignment problem) 属于0-1 整数规划,是一种特殊的整数规划问题。指派问题的标准形式(以人和事为例) 是:有n个人和n个事,已知第i个人做第j件事的费用为Cij (i; j = 1, 2,…n),要求确定人和事之间的一一对应的指派方案,使完成n件事的总费用最少。指派问题是运输问题中的一种特殊情况,"派合适的人去做合适的事"是对该问题的最贴切描述。工人相当于产地,工作相当于销地,那么劳动就是产品,工资就是运费。所以,运输问题所拥有的特点指派问题都有,而特殊之处就是在指派问题中,各个产地和销地的产量和需求量都是"1"。这个全新的特点使得其解法在运输问题解法的基础上得到了简化。

2.1 指派问题数学模型

最优指派问题也称为最优匹配问题,它是运输问题的特殊情况。问题描述如下:

设有\(n\)个人,计划做\(n\)项工作,其中\(c_{ij}\) 表示第\(i\)个人做第\(j\)项工作的收益,现求一种指派方式,使得每个人完成一项工作,总收益最大。设决策变量为\(x_{ij}\)当第\(i\)个人做第\(j\)项工作时,决策变量\(x_{ij}\)=1;否则,决策变量\(x_{ij}\)=0,因此最优指派问题可以写成0-1规划模型:

\(max\) \(z\) = \(\sum_{i=1}^n\)\(\sum_{j=1}^n\)\(c_{ij}\)\(x_{ij}\)

\(\sum_{i=1}^n\)\(x_{ij}\)=1 \(j=1,2,…,n\)

\(\sum_{j=1}^n\)\(x_{ij}\)=1 \(i=1,2,…,n\)

\(x_{ij}\)=1 or 0   \(i,j=1,2,…,n\)

如果\(c_{ij}\)表示第i个人做第j项工作所用的花费,则目标函数为求最小。

例2: 某工厂有6名工人 ,分别操作6台车床。已知每个工人Ai(i = 1, 2,…,6)对每台机床Bj(j = 1,2,…,6) 的每小时单产量为\(c_{ij}\)(i; j = 1,2,…,6)。每小时单产量如下表(这里-99表示不会操作),求产值最大的分配方案。

     [,1] [,2] [,3] [,4] [,5] [,6]
[1,]   20   15   16    5    4    7
[2,]   17   15   33   12    8    6
[3,]    9   12   18   16   30   13
[4,]   12    8   11   27   19   14
[5,]  -99    7   10   21   10   32
[6,]  -99  -99  -99    6   11   13

2.2 R处理包和函数

#R中lpSolve包提供了函数lp.assign() 来求解标准指派问题,其用法如下:
library(lpsolve)
lp.assign(cost.mat,direction = "min", presolve = 0, compute.sens = 0)
#其中,cost.mat为指派问题的系数矩阵,其元素的意义根据实际情况而定,可以是费用、时间、成本等。direction 为逻辑变量,来决定求总费用的最大值还是最小值,默认求总费用的最小值;compute.sens决定是否进行灵敏度分析。

2.3 R处理程序

library(lpSolve)
z<-c( 20,  15 , 16 ,  5,   4,   7,  17,  15,  33,  12 , 8 ,  6,   9,  12,  18,  16,  30,  13,  12,   8,  11,  27,  19,  14 ,-99,   7,  10,  21,  10,  32, -99, -99, -99,   6,  11,  13)
cost<-matrix(z,nrow=6,byrow=T)
assign.sol<-lp.assign(cost.mat=cost,direction="max")


assign.sol
#Success: the objective function is 135
assign.sol$solution
     [,1] [,2] [,3] [,4] [,5] [,6]
[1,]    1    0    0    0    0    0
[2,]    0    0    1    0    0    0
[3,]    0    1    0    0    0    0
[4,]    0    0    0    1    0    0
[5,]    0    0    0    0    0    1
[6,]    0    0    0    0    1    0

最优分配:第1个人做第1项工作;第2个人做第3项工作;第3个人做第2项工作;第4个人做第4项工作;第5个人做第6项工作;第6个人做第5项工作。

3. 指派问题实例

例3: 某商业公司计划开办5家新商店。为了尽早建成营业,商业公司决定由6家建筑公司分别承建。已知建筑公司Ai(i = 1, 2,…,5) 对新商店Bj(j = 1,2,…,5) 的建造费用的报价(万元) 为\(c_{ij}\)(i; j = 1,2,…,5)。该公司应对5家建筑公司怎样分配建筑任务,才能使总的建筑费用最少?

3.1 费用矩阵

    [,1] [,2] [,3] [,4] [,5]
[1,]    4    8    7   18   12
[2,]    7    9   17   14   10
[3,]    6    9   12    8    7
[4,]    6    7   14    6   10
[5,]    6    9   12   10    6

3.2 R计算求解

library(lpSolve)
x=matrix(c(4,7,6,6,6,8,9,9,7,9,7,17,12,14,12,18,14,8,6,10,12,10,7,10,6),ncol=5)              ##指派问题的系数矩阵
lp.assign(x)


lp.assign(x)
Success: the objective function is 34
assign.sol<-lp.assign(x,direction="min")
assign.sol$solution
     [,1] [,2] [,3] [,4] [,5]
[1,]    0    0    1    0    0
[2,]    0    1    0    0    0
[3,]    1    0    0    0    0
[4,]    0    0    0    1    0
[5,]    0    0    0    0    1

从运行结果可以看出,最优解已经成功找到。最优指派方案是:A1 承建B3,A2 承建B2,A3 承建B1,A4 承建B4,A5 承建B5。这样安排能使总费用最少,为7 + 9 + 6 + 6 + 6 = 34 万元。

4. 总结

指派问题是特殊的运输问题,是运输问题的离散化表达。在实际应用中,常会遇到各种非标准形式的指派问题,有时不能直接调用函数,处理方法是将它们化为标准形式(胡运权, 2007),然后再通过标准方法求解。同运输问题一样,有许多软件可以求解这一类问题,比如LINGO 在解决指派问题时,也必须通过各种命令建立数据集、模型、目标函数、约束函数等,比较繁琐,相比之下,R两三句代码就可以快速解决问题,较之LINGO 软件,的确方便快捷了许多。

原创文献

1.运筹学笔记 运输问题

2.(【R语言在最优化中的应用】lpSolve包解决 指派问题和指派问题)[https://cloud.tencent.com/developer/article/1411788]

3.运筹说 第39期 | 运输问题经典例题讲解