python在运筹学中的应用(一)

运筹学简介

运筹学概念

​ 运筹学,是现代管理学的一门重要专业基础课。它是20世纪30年代初发展起来的一门新兴学科,其主要目的是在决策时为管理人员提供科学依据,是实现有效管理、正确决策和现代化管理的重要方法之一。该学科应用于数学和形式科学的跨领域研究,利用统计学、数学模型和算法等方法,去寻找复杂问题中的最佳或近似最佳的解答。

​ 运筹学经常用于解决现实生活中的复杂问题,特别是改善或优化现有系统的效率。 研究运筹学的基础知识包括实分析、矩阵论、随机过程、离散数学和算法基础等。而在应用方面,多与仓储、物流、算法等领域相关。因此运筹学与应用数学、工业工程、计算机科学、经济管理等专业相关。

运筹学与数学模型关系

运筹学是数学模型的真子集

运筹学学科内容

​ 主要分支有:数学规划、图论、博弈论、决策论、排队论等

​ 应用运筹学处理问题时分为5个阶段:

①规定目标和明确问题:包括把整个问题分解成若干子问题,确定问题的尺度、有效性度量、可控变量和不可控变量,以及用来表示变量界限和变量间关系的常数和参数。

②收集数据和建立模型:包括定义关系、经验关系和规范关系。

③求解模型和优化方案:包括确定求解模型的数学方法,程序设计和调试,仿真运行和方案选优。

④检验模型和评价解答:包括检验模型的一致性、灵敏度、似然性和工作能力,并用试验数据来评价模型的解。一致性是指主要参数变动时(尤其是变到极值时)模型得出的结果是否合理;灵敏度是指输入发生微小变化时输出变化的相对大小是否合适;似然性是指对于真实数据的案例,模型是否适应;工作能力则是指模型是否容易解出,即在规定时间内算出所需的结果。

⑤方案实施和不断优化:包括应用所得的解解决实际问题,并在方案实施过程中发现新的问题和不断进行优化。上述5个阶段往往需要交叉进行,不断反复。

python在数学规划中的应用

数学规划基础知识

数学规划包括

线性规划、非线性规划、整数规划、目标规划、动态规划等。

分类 应用
线性规划 主要解决生产计划问题,合理下料问题,最优投资问题
非线性规划 如证券投资组合优化:如何合理投资使风险最小
动态规划 分阶段决策问题,比较经典的是最短路径问题
整数规划 在线性规划的基础上,变量加上整数约束,比较经典的是0-1背包问题
目标规划 多目标并存;应用范围较广,企业管理中经常会用到

建模条件

  1. 优化条件:问题所要达到的目标能用函数描述,且能用极值(max或者min)来表示

  2. 限定条件:达到目标受到一定的限制,且这些限制能够用决策变量的等式或者不等式表示

  3. 选择条件:有多种可选择的方案供决策者选择,以便找出最优方案

建模步骤

  1. 根据影响所要达到目的的因素找到决策变量

  2. 由决策变量和所在达到目的之间的函数关系确定目标函数,明确是max还是min;

  3. 由决策变量所受的限制条件确定决策变量所要满足的约束条件

线性规划

当我们得到的数学模型的目标函数为线性函数,约束条件为线性等式或不等式时称此数学模型为线性规划模型。

​ 线性规划问题可以手解的方法有“图解法“和”单纯形法”。“图解法”顾名思义,就是画图求出最优解;”单纯形法”是一个特别有趣的方法,具体操作请参考后文的参考资料(3)~

用python解决线性规划步骤

step1:将模型表达式转化为标准模式
m i n c T x s . t . { A u b x ≤ b u b A e q ∗ x = b e q l ≤ x ≤ u min \quad c^Tx\\ s.t. \quad \begin{cases} A_{ub}x \leq b_{ub} \\ A_{eq}*x = b_{eq} \\ l \leq x \leq u \end{cases} mincTxs.t.AubxbubAeqx=beqlxu

其中x是决策变量的向量; c, b_ub, b_eq, l,和 u是向量;和 Aub 和 Aeq 是矩阵

step2:Python求解

from scipy import optimize
import numpy as np

#求解函数
res = optimize.linprog(c,Aub,bub,Aeq,beq,l,u,X0,OPTIONS)
#目标函数最小值
print(res.fun)
#最优解
print(res.x)

需要注意的问题:

1、在线性规划问题中,标准形式中目标函数表达式为max;在python函数中,标准形式中目标函数表达式为min!!!在写python表达式时,请一定要格外注意c值的正负问题~~!!!

2、标准形式中,弹性约束的符号是小于等于,请一定要注意Aub矩阵每一行的正负问题

扩展小知识1:

MATLAB求解代码如下:

[x,fval]=linprog(c,A,b,Aeq,beq,l,u,X0,OPTIONS)

例1

生产安排模型:某工厂要安排生产Ⅰ、Ⅱ两种产品,已知生产单位产品所需的设备台时及A、B两种原材料的消耗,如表所示,表中右边一列是每日设备能力及原材料供应的限量,该工厂生产一单位产品Ⅰ可获利2元,生产一单位产品Ⅱ可获利3元,问应如何安排生产,使其获得最多?

设备 1 2 8(台时)
原材料A 4 0 16(kg)
原材料B 0 4 12(kg)
利润(元/kg) 2 3

假定:工厂分别生产产品Ⅰ和Ⅱx_1、x_2

模型可记为:
m a x z = 2 x 1 + 3 x 2 s . t . { x 1 + 2 x 2 ≤ 8 4 x 1 ≤ 16 4 x 2 ≤ 12 x 1 , x 2 ≥ 0 max \quad z=2x_1+3x_2\\ s.t. \quad \begin{cases} x_1+2x_2 \leq 8 \\ 4x_1 \leq 16 \\ 4x_2 \leq 12 \\ x_1,x_2 \geq 0 \end{cases} maxz=2x1+3x2s.t.x1+2x284x1164x212x1,x20
python代码如下:

#导入包
from scipy import optimize
import numpy as np

c = np.array([2,3])
Aub = np.array([[1,2],[4,0],[0,4]])
bub = np.array([8,16,12])

#求解
res = optimize.linprog(-c,Aub,bub)
print(res)

结果如下:

#等式约束的残差 
con: array([], dtype=float64)
#目标函数的最佳值
fun: -13.999999982532964
message: 'Optimization terminated successfully.'
#在所有阶段中执行的迭代总数
nit: 4
#不等式约束的松弛值
slack: array([1.02599227e-08, 1.66172107e-08, 4.00000001e+00])
#表示算法退出状态的整数(0:优化成功终止)
status: 0
#当算法成功找到最佳解决方案时为True
success: True
#在满足约束的情况下将目标函数最小化的决策变量的值
x: array([4., 2.])

扩展知识2:

​ 在线性规划中有一个很重要的知识点就是“对偶”的概念。

​ 比如说,下面的例子换一种问法:若厂长决定不生产Ⅰ和Ⅱ两种产品,决定向外销售两种原料,只收加工费,那么两种原料如何定价才是最佳决策?

​ 由于“对偶”问题时一个纯数学问题,其意义在于讨论“影子价格”的问题,但其中的模型变化、性质等十分繁复,只要写出来标准形式的数学模型,在python实现方面没有什么变化,所以这里就过多赘述了,如果感兴趣的小伙伴,可以看一下后文的参考资料(5)~

​ 注:影子价格:是在取得原问题最优解后,在其它条件不变的情况下(最重要的是最优基不变),某一资源限制被增加了一个单位,其它资源的限制不变,给目标函数增加的收益。

扩展知识3:

​ 在线性规划的领域中,还有一类经典问题——“运输问题”,包括产销平衡和产销不平衡,在数学角度解决“运输问题”有一个方法叫做“表上作业法”,也是一个很有意思的纸上画画画的过程,虽然形式上有所变化,但其根本依然是“单纯型法”。请参照后文的参考资料(6)

扩展小知识4:

​ 目标规划问题也可以通过增加正负偏差变量,变为线性规划问题进行python编程解决哦~

非线性规划

python函数

scipy.optimize.minimize(fun, x0, args=(), method=None, jac=None, hess=None, hessp=None, bounds=None, constraints=(), tol=None, callback=None, options=None)

重要参数解析:

参数 解释
fun 最小化的目标函数
x0 变量初始值数组,ndarray(n)
method 默认SLSQP(最小二乘)
constraints 约束条件

例2

求下式最小值:
2 + x 1 1 + x 2 − 3 x 1 + 4 x 3 ( 0.1 ≤ x 1 , x 2 , x 3 ≤ 0.9 ) \frac{2+x_1}{1+x_2}-3x_1+4x_3 \quad (0.1 \leq x_1,x_2,x_3 \leq 0.9) 1+x22+x13x1+4x3(0.1x1,x2,x30.9)
python代码如下:

from scipy.optimize import minimize
import numpy as np
 
def fun(args):
    a,b,c,d=args
    v=lambda x: (a+x[0])/(b+x[1]) -c*x[0]+d*x[2]
    return v
def con(args):
    # 约束条件 分为eq 和ineq
    #eq表示 函数结果等于0 ; ineq 表示 表达式大于等于0  
    x1min, x1max, x2min, x2max,x3min,x3max = args
    cons = ({'type': 'ineq', 'fun': lambda x: x[0] - x1min},\
              {'type': 'ineq', 'fun': lambda x: -x[0] + x1max},\
             {'type': 'ineq', 'fun': lambda x: x[1] - x2min},\
                {'type': 'ineq', 'fun': lambda x: -x[1] + x2max},\
            {'type': 'ineq', 'fun': lambda x: x[2] - x3min},\
             {'type': 'ineq', 'fun': lambda x: -x[2] + x3max})
    return cons
 
if __name__ == "__main__":
    #定义常量值
    args = (2,1,3,4)  #a,b,c,d
    #设置参数范围/约束条件
    args1 = (0.1,0.9,0.1, 0.9,0.1,0.9)
    cons = con(args1)
    #设置初始猜测值  
    x0 = np.asarray((0.5,0.5,0.5))
    
    res = minimize(fun(args), x0,method='SLSQP',constraints=cons)

    print(res)

结果如下:

#目标函数的最佳值
fun: -0.773684210526435
#函数的雅可比矩阵
jac: array([-2.47368421, -0.80332409,  4.        ])
message: 'Optimization terminated successfully'
nfev: 8
nit: 2
njev: 2
#表示算法退出状态的整数(0:优化成功终止)
status: 0
success: True
#在满足约束的情况下将目标函数最小化的决策变量的值
x: array([0.9, 0.9, 0.1])

整数规划

整数规划特点

  1. 原线性规划有最优解,当自变量限制为整数后,其整数规划解出现下述情况:

​ ①原线性规划最优解全是整数,则整数规划最优解与线性规划最优解一致

​ ②整数规划无可行解

  1. 整数规划最优解不能按照实数最优解简单取整而得

求解方法

  1. 分枝定界法——可求纯或混合整数线性规划问题

  2. 割平面法——可求纯或混合整数线性规划

  3. 隐枚举法——求解“0-1”整数规划:

​ ①过滤隐枚举法;

​ ②分枝隐枚举法。

  1. 匈牙利法—解决指派问题(“0-1”规划特殊情形)。
  2. 蒙特卡洛法—求解各种类型规划。

例3

​ 有一份中文说明书,需译成英、日、德、俄四种文字,分别记作A、B、C、D。现有甲、乙、丙、丁四人,他们将中文说明书译成不同语种的说明书所需时间如下表所示,问如何分派任务可是总时间最少?

A B C D
6 7 11 2
4 5 9 8
3 1 10 4
5 9 8 2

系数矩阵:
python在运筹学中的应用(一)_第1张图片
匈牙利变换后的最优解矩阵:
python在运筹学中的应用(一)_第2张图片
匈牙利法的python函数

scipy.optimize.linear_sum_assignment(cost_matrix, maximize=False)

Python代码:

from scipy.optimize import linear_sum_assignment
  
cost = np.array([[6,7,11,2],[4,5,9,8],[3,1,10,4],[5,9,8,2]])
row_ind,col_ind=linear_sum_assignment(cost)
print(row_ind)#开销矩阵对应的行索引
print(col_ind)#对应行索引的最优指派的列索引
print(cost[row_ind,col_ind])#提取每个行索引的最优指派列索引所在的元素,形成数组
print(cost[row_ind,col_ind].sum())#数组求和

结果如下:

[0 1 2 3]
[3 0 1 2]
[2 4 1 8]
#最后的最优解,即总时间为15
15

参考资料

(1) 运筹学(管理类专业基础课) - 百度百科

(2) 运筹学-MBA智库

(3) 单纯型法手算详解

(4) scipy.optimize.linprog(官方文档)

(5) 线性规划的对偶问题 - 知乎

(6) 表上作业法

(7) scipy.optimize.minimize (官方文档)

(8) scipy.optimize.linear_sum_assignment(官方文档)

你可能感兴趣的:(算法,python,算法)