首页 > 编程语言 >运输问题和指派问题——Python实现

运输问题和指派问题——Python实现

时间:2023-11-26 10:35:05浏览次数:38  
标签:Python 指派 问题 range ij 0.0 var pulp row

随着社会和经济的不断进步,现代物流业蓬勃发展,如何充分利用时间、信息、仓储、配送和联运体系创造更多的价值,是物流运作必须解决的问题。日益复杂的运输活动使得运输问题变得越来越庞杂,但是其核心思想仍然是实现现有资源的最优化配置。运输问题经常出现在计划货物配送和从某些供给地区到达需求地区之间的服务中,特别是每个供给地区(起点)的货物可获得量是有限的,每个需求地区(目的地)的货物需求量是已知的,运输问题中最常用的目标是要使货物从起点到目的地的运输成本最低。指派问题是运输问题的一种特殊情况,通常涉及到多个任务和多个执行者之间的任务分配,如将工作分配给机器,对代理分配任务,将销售人员分配给销售区域,将合同分配给投标人,等等。

一、运输问题

运输问题(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 Python计算程序

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

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

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

Python代码及运行结果如下:

import pulp
import numpy as np
from pprint import pprint

def transportation_problem(costs, x_max, y_max):

    row = len(costs)
    col = len(costs[0])
    prob = pulp.LpProblem('Transportation Problem', sense=pulp.LpMinimize)
    var = [[pulp.LpVariable(f'x{i}{j}', lowBound=0, cat=pulp.LpInteger) for j in range(col)] for i in range(row)]
    flatten = lambda x: [y for l in x for y in flatten(l)] if type(x) is list else [x]
    prob += pulp.lpDot(flatten(var), costs.flatten())
    for i in range(row):
        prob += (pulp.lpSum(var[i]) == x_max[i])
    for j in range(col):
        prob += (pulp.lpSum([var[i][j] for i in range(row)]) == y_max[j])

    prob.solve()
    return {'objective':pulp.value(prob.objective), 'var': [[pulp.value(var[i][j]) for j in range(col)] for i in range(row)]}

if __name__ == '__main__':
    costs = np.array([[4, 12, 4, 11],
                      [2, 10, 3, 9],
                      [8, 5, 11, 6]])
    max_plant = [16, 10, 22]
    max_cultivation = [8, 14, 12, 14]
    res = transportation_problem(costs, max_plant, max_cultivation)

    print(f'最小值为{res["objective"]}')
    print('各变量的取值为:')
    pprint(res['var'])

1.3 计算结果

最小值为244.0
各变量的取值为:
[[0.0, 0.0, 12.0, 4.0], [8.0, 0.0, 0.0, 2.0], [0.0, 14.0, 0.0, 8.0]]

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

二、运输问题实例

例2:某制药公司在全国设有六个生产基地,这些生产基地每天将这些药分别运往八个地区的经销部门,已知从每个生产基地到各销售部门每箱药品的运价如下表所示,问该制药公司应如何调运(运输方案),使在满足各销售部门需要的情况下,总的运输费用最少?

\(c_{ij}\) B1 B2 B3 B4 B5 B6 B7 B8 产量\(a_i\)
A1 6 2 6 7 4 2 5 9 60
A2 4 9 5 3 8 5 8 2 55
A3 5 2 1 9 7 4 3 3 51
A4 7 6 7 3 9 2 9 1 43
A5 2 3 9 5 7 2 6 5 41
A6 5 5 2 2 8 1 4 3 52
销量\(b_j\) 35 37 22 32 41 32 43 38

该问题是供大于求的不平衡运输问题。

2.1 Python计算程序

import pulp
import numpy as np
from pprint import pprint

def transportation_problem(costs, x_max, y_max):

    row = len(costs)
    col = len(costs[0])
    prob = pulp.LpProblem('Transportation Problem', sense=pulp.LpMinimize)
    var = [[pulp.LpVariable(f'x{i}{j}', lowBound=0, cat=pulp.LpInteger) for j in range(col)] for i in range(row)]
    flatten = lambda x: [y for l in x for y in flatten(l)] if type(x) is list else [x]
    prob += pulp.lpDot(flatten(var), costs.flatten())
    for i in range(row):
        prob += (pulp.lpSum(var[i]) <= x_max[i])
    for j in range(col):
        prob += (pulp.lpSum([var[i][j] for i in range(row)]) == y_max[j])

    prob.solve()
    return {'objective':pulp.value(prob.objective), 'var': [[pulp.value(var[i][j]) for j in range(col)] for i in range(row)]}

if __name__ == '__main__':
    costs = np.array([[6,2,6,7,4,2,5,9],
                      [4,9,5,3,8,5,8,2],
                      [5,2,1,9,7,4,3,3],
                      [7,6,7,3,9,2,7,1],
                      [2,3,9,5,7,2,6,5],
                      [5,5,2,2,8,1,4,3]])
    max_plant = [60,55,51,43,41,52]
    max_cultivation = [35,37,22,32,41,32,43,38]
    res = transportation_problem(costs, max_plant, max_cultivation)

    print(f'最小值为{res["objective"]}')
    print('各变量的取值为:')
    pprint(res['var'])

2.2 Pyhton计算结果

最小值为664.0
各变量的取值为:
[[0.0, 19.0, 0.0, 0.0, 41.0, 0.0, 0.0, 0.0],
 [1.0, 0.0, 0.0, 32.0, 0.0, 0.0, 0.0, 0.0],
 [0.0, 11.0, 0.0, 0.0, 0.0, 0.0, 40.0, 0.0],
 [0.0, 0.0, 0.0, 0.0, 0.0, 5.0, 0.0, 38.0],
 [34.0, 7.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
 [0.0, 0.0, 22.0, 0.0, 0.0, 27.0, 3.0, 0.0]]

由R输出结果可知6个生产点8个销售点的最小运费为664,相应的运送方案为A1→B2:19个单位,A1→B5:41个单位,A2→B1:1个单位,A2→B4:32个单位,A3→B2:11个单位,A3→B7:40个单位,A4→B6:5个单位,A4→B8:38个单位,A5→B1:34个单位,A5→B2:7个单位,A6→B3:22单位,A6→B6:27个单位,A6→B7:3个单位。

三、指派问题

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

3.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项工作所用的花费,则目标函数为求最小。
例3: 某工厂有6名工人 ,分别操作6台车床。已知每个工人Ai(i = 1, 2,…,6)对每台机床Bj(j = 1,2,…,6) 的每小时单产量为\(c_{ij}\)(i; j = 1,2,…,6)。每小时单产量如下表(这里-99表示不会操作),求产值最大的分配方案。

[[ 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]]

3.2 Python计算程序

import pulp
import numpy as np
from pprint import pprint
def assignment_problem(efficiency_matrix):
    row = len(efficiency_matrix)
    col = len(efficiency_matrix[0])
    flatten = lambda x: [y for l in x for y in flatten(l)] if type(x) is list else [x]
    m = pulp.LpProblem('assignment', sense=pulp.LpMaximize)
    var_x = [[pulp.LpVariable(f'x{i}{j}', cat=pulp.LpBinary) for j in range(col)] for i in range(row)]
    m += pulp.lpDot(efficiency_matrix.flatten(), flatten(var_x))
    for i in range(row):
        m += (pulp.lpDot(var_x[i], [1]*col) == 1)
    for j in range(col):
        m += (pulp.lpDot([var_x[i][j] for i in range(row)], [1]*row) == 1)
    m.solve()
    #print(m)
    return {'objective':pulp.value(m.objective), 'var': [[pulp.value(var_x[i][j]) for j in range(col)] for i in range(row)]}

efficiency_matrix = np.array([[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]])
res = assignment_problem(efficiency_matrix)
print(f'最大值{res["objective"]}')
print(res['var'])

3.3 Pyhton计算结果

最大值135.0
[[1.0, 0.0, 0.0, 0.0, 0.0, 0.0], 
[0.0, 0.0, 1.0, 0.0, 0.0, 0.0], 
[0.0, 1.0, 0.0, 0.0, 0.0, 0.0], 
[0.0, 0.0, 0.0, 1.0, 0.0, 0.0],
 [0.0, 0.0, 0.0, 0.0, 0.0, 1.0],
 [0.0, 0.0, 0.0, 0.0, 1.0, 0.0]]

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

四、指派问题实例

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

4.1 费用矩阵

  [[ 4  8  7 18 12]
 [ 7  9 17 14 10]
 [ 6  9 12  8  7]
 [ 6  7 14  6 10]
 [ 6  9 12 10  6]]

4.2 Python计算程序

import pulp
import numpy as np
from pprint import pprint
def assignment_problem(efficiency_matrix):
    row = len(efficiency_matrix)
    col = len(efficiency_matrix[0])
    flatten = lambda x: [y for l in x for y in flatten(l)] if type(x) is list else [x]
    m = pulp.LpProblem('assignment', sense=pulp.LpMinimize)
    var_x = [[pulp.LpVariable(f'x{i}{j}', cat=pulp.LpBinary) for j in range(col)] for i in range(row)]
    m += pulp.lpDot(efficiency_matrix.flatten(), flatten(var_x))
    for i in range(row):
        m += (pulp.lpDot(var_x[i], [1]*col) == 1)
    for j in range(col):
        m += (pulp.lpDot([var_x[i][j] for i in range(row)], [1]*row) == 1)
    m.solve()
    #print(m)
    return {'objective':pulp.value(m.objective), 'var': [[pulp.value(var_x[i][j]) for j in range(col)] for i in range(row)]}

efficiency_matrix = np.array([[4,8,7,18,12],
                             [7,9,17,14,10],
                               [6,9,12,8,7],
                              [6,7,14,6,10],
                             [6,9,12,10,6]])
res = assignment_problem(efficiency_matrix)
print(f'最小值{res["objective"]}')
print(res['var'])

4.3 Pyhton计算结果

最小值34.0
[[0.0, 0.0, 1.0, 0.0, 0.0], 
[0.0, 1.0, 0.0, 0.0, 0.0], 
[1.0, 0.0, 0.0, 0.0, 0.0], 
[0.0, 0.0, 0.0, 1.0, 0.0], 
[0.0, 0.0, 0.0, 0.0, 1.0]]

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

五、总结

运用运筹学的知识和方法对运输问题进行资源的优化配置和人员的合理分配,不仅可以为企业节省成本,提高效益和利润还可以节约运输的时间,为科学管理带来决策支持,为广大的消费者带来优惠和便利。指派问题是特殊的运输问题,是运输问题的离散化表达。在实际应用中,常会遇到各种非标准形式的指派问题,有时不能直接调用函数,处理方法是将它们化为标准形式(胡运权, 2007),然后再通过标准方法求解。同运输问题一样,有许多软件可以求解这一类问题,比如LINGO 在解决指派问题时,也必须通过各种命令建立数据集、模型、目标函数、约束函数等,比较繁琐,相比之下,R两三句代码就可以快速解决问题,较之LINGO 软件,的确方便快捷了许多。

参考文献

1.运筹学笔记 运输问题
2.【R语言在最优化中的应用】lpSolve包解决 指派问题和指派问题
3.运筹说 第39期 | 运输问题经典例题讲解
4.R语言与优化模型:线性规划、整数规划和运输问题
5.Python数学建模PuLP库线性规划入门示例详解
6.【数学建模】线性规划各种问题的Python调包方法

标签:Python,指派,问题,range,ij,0.0,var,pulp,row
From: https://www.cnblogs.com/haohai9309/p/17856512.html

相关文章

  • python 控制台 等待用户输入
    Python控制台等待用户输入的实现方法1.总览在Python中,要实现控制台等待用户输入的功能,可以使用input()函数来实现。input()函数会暂停程序的执行,直到用户输入一条信息并按下回车键。本文将详细介绍如何使用input()函数实现这一功能。2.实现步骤下表展示了整个实现过程的步骤......
  • python 矩阵 换行
    Python矩阵换行实现流程为了帮助初学者实现Python矩阵的换行,下面将提供一个详细的步骤,通过代码和注释的形式指导他们完成这个任务。在开始之前,确保已经了解Python的基本语法和矩阵的基本概念。步骤步骤描述步骤1创建一个二维矩阵步骤2使用循环遍历二维矩阵的每一......
  • python 解密linux密码
    Python解密Linux密码简介在Linux系统中,用户的密码通常被加密存储在/etc/shadow文件中,以确保用户密码的安全性。这种加密方式称为密码哈希算法,它将用户密码转换为一串不可逆的密文。然而,有时候我们需要解密这些密码,例如在恢复用户密码或进行密码破解时。本文将介绍如何使用Python......
  • python 截取xlsx文件中某个时间段的数据
    Python截取xlsx文件中某个时间段的数据引言在日常工作和数据分析中,我们经常需要处理各种各样的数据文件。而其中一种较为常见的文件格式是Excel文件,尤其是.xlsx文件。Python作为一种强大的编程语言,提供了丰富的库和工具来处理Excel文件。本文将介绍如何使用Python截取.xlsx文件中......
  • python 将数值 0 1 转 bool
    Python将数值0和1转换为布尔值介绍在Python中,布尔值是True和False,它们是逻辑运算的结果。然而,有时我们需要将数值0和1转换为布尔值。本文将介绍如何在Python中实现这种转换,并提供代码示例。数值0和1的含义在大多数编程语言中,0通常表示False,而1通常表示True。在Python中也是......
  • python 将docx按页分割
    Python将docx按页分割在进行文档处理过程中,有时我们需要将一个大的docx文件按页分割成多个小文件,这样可以更方便地处理、管理和查看文档内容。本文将介绍如何使用Python来实现这个功能,并提供相应的代码示例。docx文档格式简介在开始介绍具体的代码实现之前,我们先来了解一下docx......
  • python 加载npz数据为numpy
    使用Python加载npz数据为numpy概述本文将教你如何使用Python加载.npz文件数据为numpy数组。.npz文件是一种特殊的numpy数组格式,它可以存储多个numpy数组,并且可以方便地读取和写入。加载.npz文件的过程相对简单,只需要几个简单的步骤即可完成。流程概述下面是加载.npz文件为numpy......
  • python 加载dll的类
    Python加载DLL的类在Python中,我们可以使用ctypes模块来加载并调用DLL(DynamicLinkLibrary)文件中的函数。DLL是一种包含可供程序调用的函数和数据的动态链接库。通过加载DLL,我们可以在Python程序中使用其他编程语言编写的功能强大的库。本文将介绍如何使用Python加载DLL的类,并提......
  • python 对象 初始化并设置默认值
    Python对象初始化并设置默认值的实现步骤在Python中,我们经常需要为对象设置默认值。这些默认值可以在对象初始化时被设置,并在对象的方法中使用。本文将介绍如何使用Python的类和对象来实现对象初始化并设置默认值的功能。我们将根据以下步骤来完成这个任务:创建一个类定义初始......
  • python 读取文件名中带有循环变量
    标题:Python中使用循环变量读取文件名的方法**摘要:**在Python编程中,我们经常需要读取并处理多个文件。而文件名中的循环变量可以帮助我们更加灵活地处理这种情况。本文将介绍如何使用Python中的循环变量来读取文件名,并给出相关的代码示例和详细说明。1.引言在实际的数据处理中,我......