avatar

Xiaojun Wu

Ph.D. Candidate, HKUST(GZ)
Affiliate LLM Researcher, IDEA Research
wuxiaojun@idea.edu.cn & wuxiaojun@pku.edu.cn & xwu647@connect.hkust-gz.edu.cn


← Blog

python数学建模实践

python运筹优化(八):运输问题

在知乎查看原文

阅读 知乎未公开 收藏 223 点赞 58

互动数据为 2026-09-13 的知乎数据快照;阅读量未在当前可用的知乎公开接口中提供。

运输问题是一类特殊的线性规划问题,一般的运输问题就是要解决:把某种产品从若干个产地调运到若干个销地,在已知每个产地的供应量、每个销地的需求量、各地之间的运输单价的前提下,如何确定一个使得总的运输费用最小的方案。具体的数学模型和笔算求解可以参考中国大学MOOC上江西财经大学的运筹学课程。这个课程包含了运筹学要求掌握的所有问题,以及这些问题的单纯形法求解、excel求解,因此理论部分的介绍都可以参考这个课程,本文的实践主要基于pyomo和geatpy编程求解。

一、产销平衡的运输问题

有三个工厂F1,F2,F3生产同种产品,它们的产量分别为25,15,10单位。该公司把这些产品分别运往四个销售点D1,D2,D3,D4 ,各销售点销量分别为13,21,9和7。从各工厂到各销售点的单位产品的运价为下表所示。问该公司应如何调运产品,在满足各销售点需求量的前提下,使总运费为最少

我们假设i产地(i属于{F1,F2,F3})运输到j销售地的数量为xij,则数学模型如下:

一般数学模型如下,其中cij是i到j的单位运价:


运输问题可以使用表上作业法进行人工求解,想要了解的可以看慕课,我们这里主要是讲述怎么利用编程工具pyomo和geatpy进行求解。

二、pyomo求解运输问题

1、数学模型

2、定义参数

首先定义好参数

# Import of the pyomo module
from pyomo.environ import *
 
# Creation of a Concrete Model
model = ConcreteModel()


# 定义生产地和销售地
## Define sets ##
#  Sets
#       i   canning plants   / seattle, san-diego /
#       j   markets          / new-york, chicago, topeka / ;
model.i = Set(initialize=['seattle','san-diego'], doc='Canning plans')
model.j = Set(initialize=['new-york','chicago', 'topeka'], doc='Markets')


# 定义生产地的生产量和销售地的需求量
## Define parameters ##
#   Parameters
#       a(i)  capacity of plant i in cases
#         /    seattle     350
#              san-diego   600  /
#       b(j)  demand at market j in cases
#         /    new-york    325
#              chicago     300
#              topeka      275  / ;
model.a = Param(model.i, initialize={'seattle':350,'san-diego':600}, doc='Capacity of plant i in cases')
model.b = Param(model.j, initialize={'new-york':325,'chicago':300,'topeka':275}, doc='Demand at market j in cases')


# 定义生产地到销售地的距离(千英里)
#  Table d(i,j)  distance in thousands of miles
#                    new-york       chicago      topeka
#      seattle          2.5           1.7          1.8
#      san-diego        2.5           1.8          1.4  ;
dtab = {
    ('seattle',  'new-york') : 2.5,
    ('seattle',  'chicago')  : 1.7,
    ('seattle',  'topeka')   : 1.8,
    ('san-diego','new-york') : 2.5,
    ('san-diego','chicago')  : 1.8,
    ('san-diego','topeka')   : 1.4,
    }
model.d = Param(model.i, model.j, initialize=dtab, doc='Distance in thousands of miles')


# 费用/千英里
#  Scalar f  freight in dollars per case per thousand miles  /90/ ;
model.f = Param(initialize=90, doc='Freight in dollars per case per thousand miles')


# cij就是产地i到销地j的单位运价
#  Parameter c(i,j)  transport cost in thousands of dollars per case ;
#            c(i,j) = f * d(i,j) / 1000 ;
def c_init(model, i, j):
 return model.f * model.d[i,j] / 1000
model.c = Param(model.i, model.j, initialize=c_init, doc='Transport cost in thousands of dollar per case')


3、定义变量

定义变量xij:
## Define variables ##
#  Variables
#       x(i,j)  shipment quantities in cases
#       z       total transportation costs in thousands of dollars ;
#  Positive Variable x ;
model.x = Var(model.i, model.j, bounds=(0.0,None), doc='Shipment quantities in case')

4、约束条件

## Define contrains ##
# supply(i)   observe supply limit at plant i
# supply(i) .. sum (j, x(i,j)) =l= a(i)
def supply_rule(model, i):
 return sum(model.x[i,j] for j in model.j) <= model.a[i]
model.supply = Constraint(model.i, rule=supply_rule, doc='Observe supply limit at plant i')
# demand(j)   satisfy demand at market j ;  
# demand(j) .. sum(i, x(i,j)) =g= b(j);
def demand_rule(model, j):
 return sum(model.x[i,j] for i in model.i) >= model.b[j]  
model.demand = Constraint(model.j, rule=demand_rule, doc='Satisfy demand at market j')

5、目标函数

## Define Objective and solve ##
#  cost        define objective function
#  cost ..        z  =e=  sum((i,j), c(i,j)*x(i,j)) ;
#  Model transport /all/ ;
#  Solve transport using lp minimizing z ;
def objective_rule(model):
 return sum(model.c[i,j]*model.x[i,j] for i in model.i for j in model.j)
model.objective = Objective(rule=objective_rule, sense=minimize, doc='Define objective function')

6、模型求解

展示模型结果:

## Display of the output ##
# Display x.l, x.m ;
def pyomo_postprocess(options=None, instance=None, results=None):
  model.x.display()


模型求解:


# This is an optional code path that allows the script to be run outside of
# pyomo command-line.  For example:  python transport.py
if __name__ == '__main__':
 # This emulates what the pyomo command-line tools does
 from pyomo.opt import SolverFactory
 import pyomo.environ
    opt = SolverFactory("glpk")
    results = opt.solve(model)
 #sends results to stdout
    results.write()
    print("\nDisplaying Solution\n" + '-'*60)
    pyomo_postprocess(None, model, results)

模型结果:

# ==========================================================
# = Solver Results                                         =
# ==========================================================
# ----------------------------------------------------------
#   Problem Information
# ----------------------------------------------------------
Problem: 
- Name: unknown
  Lower bound: 153.675
  Upper bound: 153.675
  Number of objectives: 1
  Number of constraints: 6
  Number of variables: 7
  Number of nonzeros: 13
  Sense: minimize
# ----------------------------------------------------------
#   Solver Information
# ----------------------------------------------------------
Solver: 
- Status: ok
  Termination condition: optimal
  Statistics: 
    Branch and bound: 
      Number of bounded subproblems: 0
      Number of created subproblems: 0
  Error rc: 0
  Time: 0.03862881660461426
# ----------------------------------------------------------
#   Solution Information
# ----------------------------------------------------------
Solution: 
- number of solutions: 0
  number of solutions displayed: 0


Displaying Solution
------------------------------------------------------------
x : Shipment quantities in case
    Size=6, Index=x_index
    Key                       : Lower : Value : Upper : Fixed : Stale : Domain
     ('san-diego', 'chicago') :   0.0 :   0.0 :  None : False : False :  Reals
    ('san-diego', 'new-york') :   0.0 : 325.0 :  None : False : False :  Reals
      ('san-diego', 'topeka') :   0.0 : 275.0 :  None : False : False :  Reals
       ('seattle', 'chicago') :   0.0 : 300.0 :  None : False : False :  Reals
      ('seattle', 'new-york') :   0.0 :   0.0 :  None : False : False :  Reals
        ('seattle', 'topeka') :   0.0 :   0.0 :  None : False : False :  Reals

三、geatpy求解运输问题

1、定义参数

I = ['seattle','san-diego']
J = ['new-york','chicago', 'topeka']


# 定义生产地的生产量和销售地的需求量
a = {'seattle':350,'san-diego':600}
b = {'new-york':325,'chicago':300,'topeka':275}


# 定义生产地到销售地的距离(千英里)
#  Table d(i,j)  distance in thousands of miles
#                    new-york       chicago      topeka
#      seattle          2.5           1.7          1.8
#      san-diego        2.5           1.8          1.4  ;
dtab = {
    ('seattle',  'new-york') : 2.5,
    ('seattle',  'chicago')  : 1.7,
    ('seattle',  'topeka')   : 1.8,
    ('san-diego','new-york') : 2.5,
    ('san-diego','chicago')  : 1.8,
    ('san-diego','topeka')   : 1.4,
    }


# 费用/千英里
#  Scalar f  freight in dollars per case per thousand miles  /90/ ;
f = 90


# cij就是产地i到销地j的单位运价


def c_init(i, j):
 return f * dtab[i,j] / 1000


c = {}
for i in I:
 for j in J:
        c[i,j]=c_init(i,j)

2、约束条件

## Define contrains ##
# supply(i)   observe supply limit at plant i
# supply(i) .. sum (j, x(i,j)) =l= a(i)
def supply_rule(x, i):
 return sum(x[i,j] for j in J) - a[i]


# demand(j)   satisfy demand at market j ;  
# demand(j) .. sum(i, x(i,j)) =g= b(j);
def demand_rule(x, j):
 return (-1)* sum(x[i,j] for i in I) + b[j]


3、目标函数

# 目标函数
def objective_rule(c,x):
 return sum(c[i,j]*x[i,j] for i in I for j in J)

4、建立模型

# -*- coding: utf-8 -*-
"""MyProblem.py"""
import numpy as np
import geatpy as ea


class MyProblem(ea.Problem): # 继承Problem父类
 def __init__(self):
        name = 'MyProblem' # 初始化name(函数名称,可以随意设置)
        M = 1 # 初始化M(目标维数)
        maxormins = [1]  # 初始化maxormins(目标最小最大化标记列表,1:最小化该目标;-1:最大化该目标)
        Dim = 6 # 初始化Dim(决策变量维数),这里就是产地*销地=2*3
        varTypes = [0] * Dim  # 初始化varTypes(决策变量的类型,元素为0表示对应的变量是连续的;1表示是离散的)
        lb = [0] * Dim  # 决策变量下界
        ub = [10000] * Dim  # 决策变量上界
        lbin = [1] * Dim  # 决策变量下边界(0表示不包含该变量的下边界,1表示包含),或者写作【1】*3
        ubin = [1] * Dim  # 决策变量上边界(0表示不包含该变量的上边界,1表示包含)
 # 调用父类构造方法完成实例化
        ea.Problem.__init__(self, name, M, maxormins, Dim, varTypes, lb, ub, lbin, ubin)


 def aimFunc(self, pop): # 目标函数
        Vars = pop.Phen  # 得到决策变量矩阵


        cnt = 0
        x = {}
        for i in I:
           for j in J:
                x[i, j] = Vars[:, [cnt]]
                cnt += 1


        pop.ObjV = objective_rule(c, x)  # 计算目标函数值,赋值给pop种群对象的ObjV属性,这里就是总运费


 # 采用可行性法则处理约束
 for i in I:
            pop.CV = np.hstack([
                supply_rule(x, i)  # i产地的总供应量小于他的总生产量
            ])


 for j in J:
            pop.CV = np.hstack([
                pop.CV, demand_rule(x, j)  # j销地的总供应量要大于他的总销售量,保证供应是充足的
            ])

5、模型求解

if __name__ == '__main__':
 """================================实例化问题对象==========================="""
    problem = MyProblem() # 生成问题对象
 """==================================种群设置==============================="""
    Encoding = 'RI' # 编码方式
    NIND = 100 # 种群规模
    Field = ea.crtfld(Encoding, problem.varTypes, problem.ranges, problem.borders) # 创建区域描述器
    population = ea.Population(Encoding, Field, NIND) # 实例化种群对象(此时种群还没被初始化,仅仅是完成种群对象的实例化)
 """================================算法参数设置============================="""
    myAlgorithm = ea.soea_DE_rand_1_L_templet(problem, population) # 实例化一个算法模板对象
    myAlgorithm.MAXGEN = 500 # 最大进化代数
    myAlgorithm.mutOper.F = 0.5 # 差分进化中的参数F
    myAlgorithm.recOper.XOVR = 0.7 # 重组概率
 """===========================调用算法模板进行种群进化======================="""
    [population, obj_trace, var_trace] = myAlgorithm.run() # 执行算法模板
    population.save() # 把最后一代种群的信息保存到文件中
 # 输出结果
    best_gen = np.argmin(problem.maxormins * obj_trace[:, 1]) # 记录最优种群个体是在哪一代
    best_ObjV = obj_trace[best_gen, 1]
    print('最优的目标函数值为:%s'%(best_ObjV))
    print('最优的决策变量值为:')
 for i in range(var_trace.shape[1]):
        print(var_trace[best_gen, i])
    print('有效进化代数:%s'%(obj_trace.shape[0]))
    print('最优的一代是第 %s 代'%(best_gen + 1))
    print('评价次数:%s'%(myAlgorithm.evalsNum))
    print('时间已过 %s 秒'%(myAlgorithm.passTime))

最终结果是,可以看出153.70会比pyomo求解的结果(153.675)差一下:

种群信息导出完毕。
最优的目标函数值为:153.7070860662967
最优的决策变量值为:
148.03609859240396
299.43385407422363
0.3649843431022787
176.9952992431655
0.5781097011820691
274.6734971806611
有效进化代数:500
最优的一代是第 492 代
评价次数:50600
时间已过 0.35503411293029785 秒

参考

【1】运输问题慕课:icourse163.org/learn/JX

【2】pyomo求解运输问题:nbviewer.jupyter.org/gi


© 2025 Xiaojun Wu. All rights reserved. Powered by Jekyll and Minimal Light theme.