浅谈Python数学建模之线性规划


Posted in Python onJune 23, 2021
目录
  • 一、求解方法、算法和编程方案
    • 1.1、线性规划问题的求解方法
    • 1.2、线性规划的最快算法
    • 1.3、选择适合自己的编程方案
  • 二、PuLP库求解线性规划问题
    • 2.1、线性规划问题的描述
    • 2.2、PuLP 求解线性规划问题的步骤
    • 2.3、Python例程:线性规划问题
  • 三、小结

 

一、求解方法、算法和编程方案

线性规划 (Linear Programming,LP) 是很多数模培训讲的第一个算法,算法很简单,思想很深刻。

线性规划问题是中学数学的内容,鸡兔同笼就是一个线性规划问题。数学规划的题目在高考中也经常出现,有直接给出线性约束条件求线性目标函数极值,有间接给出约束条件求线性目标函数极值,还有已知约束条件求非线性目标函数极值问题。

因此,线性规划在数学建模各类问题和算法中确实是比较简单的问题。下面我们通过这个比较简单、也比较熟悉的问题,分析一下数学模型问题的方法、算法和学习方案。探讨这些容易混淆的概念,也便于大家理解本系列教程的初衷和特色。

 

1.1、线性规划问题的求解方法

解决线性规划问题有很多数学方法,例如:

  • 图解法, 用几何作图的方法并求出其最优解,中学就讲过这种方法,在经济学研究中十分常用;
  • 矩阵法, 引进松弛变量将线性规划问题转换成增广矩阵形式后逐次求解, 是单纯性法之前的典型方法;
  • 单纯性法, 利用多面体在可行域内逐步构造新的顶点来不断逼近最优解,是线性规划研究的里程碑,至今仍然是最重要的方法之一;
  • 内点法,通过选取可行域内部点沿下降方向不断迭代来达到最优解,是目前理论上最好的线性规划问题求解方法;
  • 启发式方法,依靠经验准则不断迭代改进来搜索最优解 ,如贪心法、模拟退火、遗传算法、神经网络。

虽然不同的求解方法都是面对线性规划问题,也就必然会殊途同归,但它们在思想上就存在着本质区别,在求解方法和步骤上也就完全不同。

不夸张地说,对于很多小白,学没学过单纯性法,对于学习启发式方法可能完全没有区别。

这意味着什么呢?这就是说,对于非数学专业的同学,对于学习数学建模的同学,针对每一类问题,完全没必要学习各种解决方法。即便是数学专业的同学,也不可能在数模学习期间把各种方法都学会。

对于小白,本文推荐选择较为通用、相对简单(思路简单、程序简单)的方法来进行学习,没必要贪多求新。

 

1.2、线性规划的最快算法

算法,跟方法有什么不同呢?

算法的定义是“解题方案的准确而完整的描述”,是一系列解决问题的清晰指令,算法代表着用系统的方法描述解决问题的策略机制。

我对“方法”的理解是思想方法,是求解问题总体框架,而“算法”是具体和明确的实现步骤,在计算机编程中相当于详细的流程图。

在每一种方法的基本思想和方案提出后,往往都会有很多变形、改进和发展的算法。极少的改进算法具有实质贡献而成为主流的经典算法,即便如此往往也只是性能、效率上的提升,对于求解数模竞赛中的问题基本没有影响。

而绝大多数改进算法只是针对某些特殊情况、特殊问题(自称)有效,常用于大量的灌水论文。对于数学建模来说,学习基本算法或者目前的经典算法就足够了,不需要听信改进算法中自称的优点,那都是莆田系的广告。

有一种例外情况,就是一些算法是有适用范围和限制条件的。举个例子,内点法的基本算法不能处理等式约束,最短路径问题中 Dijkstar算法不能处理负权边。这种情况下如果选错算法,问题是无法求解的。所以对我们来说,搞清楚算法的适用范围,比理解算法本身更重要。

回到本节的标题,对于线性规划问题,什么算法是最快的呢?答案是:猜。不是让你猜,而是说求解线性规划问题,猜起来比较快。不是开玩笑,我是认真的。

佐治亚理工学院彭泱教授在 2021年计算机理论顶会 SODA2021 获得最佳论文(Best paper award at ACM-SIAM symposium on discrete algorithms 2021),正是研究线性规划问题的求解——“Solving sparse linear systems faster than matrix multiplication”,所用的全新思路是:猜,反复猜,迭代猜。

浅谈Python数学建模之线性规划

当然,猜起来比较快只是在某些特殊条件下才有效的,至于在什么条件下猜,怎么猜,这不是我们所要关心,所能理解的问题了。只是以此说明,简单的问题也有复杂的情况,每个问题都有很多求解的思路、方法和算法。

 

1.3、选择适合自己的编程方案

编程方案是我杜撰的术语。我所要表达意思是,在选择了求解方法和算法以后,是自己按照算法步骤一步步编程实现,或者找到例程调试使用,还是调用第三方工具包/库函数来完成呢?

首先,对于学习数学建模、参加数模竞赛,不建议自己按照算法步骤去编程。我们在《01.新手必读》中讨论过这个问题,对于数学小白兼计算机小白,这样做既不可行也没必要;即使你愿意挑战自我去试试,那其实已经是走在学习另一门计算机或算法课程的路上了。

其次,要不要找到例程自己调试、使用?很多数模培训就是这么说,这么做的,而且把这些收集的例程当作核心机密吸引同学。我不反对这样做,这种学习方法对于理解算法、提高编程能力很有帮助;但是并不推荐这样做,原因是:

(1)我认为学习数学建模、参加数模竞赛,重点应该放在识别问题、分析问题、解决问题,能使用算法和编程就足够了;

(2)第三方库与例程没有本质区别,第三方库就是经典的、规范的、标准化的例程,既然选择例程为什么不选择优秀的例程——第三方库呢?

(3)大部分例程都存在很多问题,即使调试通过仍然有很多坑,而且新手难以识别。

所以我是明确推荐优选直接使用第三方库来解决问题,这也是 Python 语言“不要重复造轮子”的思想。

进一步地,很多工具包/库函数都能实现常用的算法,应该如何选择呢?

如果你对某个工具包已经很熟悉,又能实现所要的算法,这当然是理想的选择。如果你是小白,就跟着我走吧。

本系列选择第三方工具包的原则是:

(1)优选常用的工具包;

(2)优选通用功能的工具包和函数(例如,最好既能实现线性规划,又能实现整数规划、非线性规划);

(3)优选安装简单、使用简单、配置灵活的工具包;

(4)优选兼模型检验、图形绘制的工具包。

 

二、PuLP库求解线性规划问题

 

2.1、线性规划问题的描述

线性规划是研究线性等式或不等式约束条件下求解线性目标函数的极值问题,常用于解决资源分配、生产调度和混合问题。

一般线性规划问题的标准形式为:

浅谈Python数学建模之线性规划

满足所有约束条件的解,称为线性规划问题的可行解;所有可行解构成的集合,称为可行域。

使目标函数达到最小值的解,称为最优解。

线性规划问题的建模和求解,通常按照以下步骤进行:

  • 问题定义,确定决策变量、目标函数和约束条件;
  • 模型构建,由问题描述建立数学方程,并转化为标准形式的数学模型;
  • 模型求解,用标准模型的优化算法对模型求解,得到优化结果。

很多 Python 的第三方包,都提供求解线性规划问题的算法,有的工具包还提供整数规划、非线性规划的算法。例如:

  • Scipy 库提供了解简单线性或非线性规划问题,但是不能求解如背包问题的0-1规划问题,或整数规划问题,混合整数规划问题。
  • PuLP 可以求解线性规划、整数规划、0-1规划、混合整数规划问题,提供多种针对不同类型问题的求解器。
  • Cvxpy 是一种凸优化工具包,可以求解线性规划、整数规划、0-1规划、混合整数规划、二次规划和几何规划问题。

此外,SKlearn、DOcplex、Pymprog 等很多第三方工具包也都能求解线性规划问题。

 

2.2、PuLP 求解线性规划问题的步骤

例题 1:

浅谈Python数学建模之线性规划

下面以该题为例讲解 PuLP 求解线性规划问题的步骤:

(0)导入 PuLP库函数

import pulp

(1)定义一个规划问题

MyProbLP = pulp.LpProblem("LPProbDemo1", sense=pulp.LpMaximize)

pulp.LpProblem 是定义问题的构造函数。

"LPProbDemo1"是用户定义的问题名(用于输出信息)。

参数 sense 用来指定求最小值/最大值问题,可选参数值:LpMinimize、LpMaximize 。本例 “sense=pulp.LpMaximize” 表示求目标函数的最大值。

(2)定义决策变量

x1 = pulp.LpVariable('x1', lowBound=0, upBound=7, cat='Continuous') 
x2 = pulp.LpVariable('x2', lowBound=0, upBound=7, cat='Continuous')
x3 = pulp.LpVariable('x3', lowBound=0, upBound=7, cat='Continuous')

pulp.LpVariable 是定义决策变量的函数。
'x1' 是用户定义的变量名。

参数 lowBound、upBound 用来设定决策变量的下界、上界;可以不定义下界/上界,默认的下界/上界是负无穷/正无穷。本例中 x1,x2,x3 的取值区间为 [0,7]。

参数 cat 用来设定变量类型,可选参数值:'Continuous' 表示连续变量(默认值)、' Integer ' 表示离散变量(用于整数规划问题)、' Binary ' 表示0/1变量(用于0/1规划问题)。

(3)添加目标函数

MyProbLP += 2*x1 + 3*x2 - 5*x3  	# 设置目标函数

添加目标函数使用 "问题名 += 目标函数式" 格式。

(4)添加约束条件

MyProbLP += (2*x1 - 5*x2 + x3 >= 10)  # 不等式约束
MyProbLP += (x1 + 3*x2 + x3 <= 12)  # 不等式约束
MyProbLP += (x1 + x2 + x3 == 7)  # 等式约束

添加约束条件使用 "问题名 += 约束条件表达式" 格式。

约束条件可以是等式约束或不等式约束,不等式约束可以是 小于等于 或 大于等于,分别使用关键字">="、"<="和"=="。

(5)求解

MyProbLP.solve()
print("Status:", pulp.LpStatus[MyProbLP.status]) # 输出求解状态
for v in MyProbLP.variables():
    print(v.name, "=", v.varValue)  # 输出每个变量的最优值
print("F(x) = ", pulp.value(MyProbLP.objective))  #输出最优解的目标函数值

solve() 是求解函数。PuLP默认采用 CBC 求解器来求解优化问题,也可以调用其它的优化器来求解,如:GLPK,COIN CLP/CBC,CPLEX,和GUROBI,但需要另外安装。 

 

2.3、Python例程:线性规划问题

例程 1:求解线性规划问题

import pulp
MyProbLP = pulp.LpProblem("LPProbDemo1", sense=pulp.LpMaximize)  # 求最大值
x1 = pulp.LpVariable('x1', lowBound=0, upBound=7, cat='Continuous') 
x2 = pulp.LpVariable('x2', lowBound=0, upBound=7, cat='Continuous') 
x3 = pulp.LpVariable('x3', lowBound=0, upBound=7, cat='Continuous') 
MyProbLP += 2*x1 + 3*x2 - 5*x3  	# 设置目标函数
MyProbLP += (2*x1 - 5*x2 + x3 >= 10)  # 不等式约束
MyProbLP += (x1 + 3*x2 + x3 <= 12)  # 不等式约束
MyProbLP += (x1 + x2 + x3 == 7)  # 等式约束
MyProbLP.solve()  # youcans@xupt
print("Status:", pulp.LpStatus[MyProbLP.status]) # 输出求解状态
for v in MyProbLP.variables():  # youcans
    print(v.name, "=", v.varValue)  # 输出每个变量的最优值
print("Max F(x) = ", pulp.value(MyProbLP.objective))  #输出最优解的目标函数值

例程 1 运行结果:

Welcome to the CBC MILP Solver 

Version: 2.9.0 

Build Date: Feb 12 2015 

 

Status: Optimal

x1 = 6.4285714

x2 = 0.57142857

x3 = 0.0

Max F(x) =  14.57142851

例程01 程序说明:

  • 用 PuLP 库求解线性规划问题,可以选择求最大值或最小值,可以按照问题的数学描述,直接输入目标函数、等式约束和不等式约束,不等式约束可以选择 <= 或 >=,不需要进行转换。这中方式简单直观,非常适合初学者掌握。
  • 对于较大规模线性规划问题, PuLP 库支持用字典类型(dict)建立多个变量,设置目标函数和约束条件。

 

三、小结

求解线性规划问题的方法非常简单,本文实际上并未讲解具体的算法。

希望通过对求解方法、算法和编程方案的讲解,阐明作者对于数学建模学什么、怎么学的理解,也使读者能了解本系列教程的特点:本教程不打算详细讲解各种算法的具体方法,重点介绍如何使用第三方包实现算法、解决问题。

以上就是浅谈Python数学建模之线性规划的详细内容,更多关于Python 线性规划的资料请关注三水点靠木其它相关文章!

Python 相关文章推荐
python在windows下实现ping操作并接收返回信息的方法
Mar 20 Python
Python的Flask框架中Flask-Admin库的简单入门指引
Apr 07 Python
在Python的Django框架中获取单个对象数据的简单方法
Jul 17 Python
python爬虫之百度API调用方法
Jun 11 Python
Python实现的堆排序算法原理与用法实例分析
Nov 22 Python
Python smtplib实现发送邮件功能
May 22 Python
python实现对csv文件的列的内容读取
Jul 04 Python
python绘制简单彩虹图
Nov 19 Python
numpy基础教程之np.linalg
Feb 12 Python
Python for循环与getitem的关系详解
Jan 02 Python
Python面向对象多态实现原理及代码实例
Sep 16 Python
Django扫码抽奖平台的配置过程详解
Jan 14 Python
教你如何用Python实现人脸识别(含源代码)
python 对图片进行简单的处理
DjangoRestFramework 使用 simpleJWT 登陆认证完整记录
浅析Python中的套接字编程
Python中使用ipython的详细教程
详解Python类和对象内容
python析构函数用法及注意事项
Jun 22 #Python
You might like
Windows2003 下 MySQL 数据库每天自动备份
2006/12/21 PHP
php下通过curl抓取yahoo boss 搜索结果的实现代码
2011/06/10 PHP
php中转义mysql语句的实现代码
2011/06/24 PHP
利用PHP实现智能文件类型检测的实现代码
2011/08/02 PHP
WordPress中用于更新伪静态规则的PHP代码实例讲解
2015/12/18 PHP
php之可变变量的实例详解
2017/09/12 PHP
PHP获取日期对应星期、一周日期、星期开始与结束日期的方法
2018/06/22 PHP
设置下载不需要倒计时cookie(倒计时代码)
2008/11/19 Javascript
jquery全选checkBox功能实现代码(取消全选功能)
2013/12/10 Javascript
js面向对象之公有、私有、静态属性和方法详解
2015/04/17 Javascript
微信小程序  简单实例(阅读器)的实例开发
2016/09/29 Javascript
Nodejs读取文件时相对路径的正确写法(使用fs模块)
2017/04/27 NodeJs
JavaScript ES6中const、let与var的对比详解
2017/06/18 Javascript
mac上node.js环境的安装测试
2017/07/03 Javascript
layui使用form表单实现post请求页面跳转的方法
2019/09/14 Javascript
layui加载数据显示loading加载完成loading消失的实例代码
2019/09/23 Javascript
Python如何判断数独是否合法
2016/09/08 Python
python OpenCV学习笔记实现二维直方图
2018/02/08 Python
浅谈Python中的私有变量
2018/02/28 Python
Python 解决中文写入Excel时抛异常的问题
2018/05/03 Python
使用python实现mqtt的发布和订阅
2019/05/05 Python
Django:使用filter的pk进行多值查询操作
2020/07/15 Python
python中的垃圾回收(GC)机制
2020/09/21 Python
html5时钟实现代码
2010/10/22 HTML / CSS
HTML5+lufylegend实现游戏中的卷轴
2016/02/29 HTML / CSS
戴尔荷兰官方网站:Dell荷兰
2020/10/04 全球购物
委托证明的格式
2014/01/10 职场文书
仓库组长岗位职责
2014/01/29 职场文书
会计求职信
2014/05/29 职场文书
介绍信样本
2015/01/31 职场文书
美术教师求职信范文
2015/03/20 职场文书
天那边观后感
2015/06/09 职场文书
青年教师听课心得体会
2016/01/15 职场文书
祝福语集锦:给满月宝宝的祝福语
2019/11/20 职场文书
Python selenium模拟网页点击爬虫交管12123违章数据
2021/05/26 Python
如何用Laravel包含你自己的帮助函数
2021/05/27 PHP