线性规划之单纯形算法矩阵描述与python实现
声明
本文为本人原创,转载请注明出处。本文仅发表在博客园,作者LightningStar。
问题描述
所有的线性规划问题都可以归约到标准型的问题,规约过程比较简单且已经超出本文范围,不再描述,可以参考拓展阅读部分。下面直接给出线性规划标准型描述。
标准型描述
线性规划问题标准型的矩阵描述[1]:
目标:
\]
约束:
\mathbf{x} \geq \mathbf{0}
\]
我们的最终目标在约束条件下就是找到一个解\(\mathbf{x}\),使得\(z\)最大。注意我们的描述,我们的解是找到一组值\(\mathbf{x}\)使得\(z\)最大,这组值才是问题的一个解,\(z\)得最大值究竟是多少并不是问题的解。
在后文中,粗体小写字母一般表示向量,粗体大写字母一般表示矩阵,小写字母表示标量。
松弛型
在用单纯型法求解线性规划问题之前,必须先把线性规划问题转换成增广矩阵形式。增广矩阵形式引入非负松弛变量将不等式约束变成等式约束。
引入松弛变量\(\mathbf{\hat{x}} = \mathbf{b} - \mathbf{Ax}\),则有:
object: \qquad maximize \quad z = \mathbf{c^Tx}\\
subject: \qquad \mathbf{\hat{x}} = \mathbf{b} - \mathbf{Ax} \\
\qquad \qquad \qquad \mathbf{x} \geq \mathbf{0}, \mathbf{\hat{x}} \geq \mathbf{0}
\end{align}
\]
将上述公式表示为矩阵形式则为:
\begin{bmatrix}
1 & \mathbf{c^T} & \mathbf{0} \\
\mathbf{0} & \mathbf{A} & \mathbf{I}
\end{bmatrix}
\begin{bmatrix}
-z \\
\mathbf{x} \\
\mathbf{\hat{x}}
\end{bmatrix} =
\begin{bmatrix}
\mathbf{0} \\
\mathbf{b}
\end{bmatrix}
\end{equation} \\
其中,z为需要求最大值的变量,\mathbf{x, \hat{x}} \geq 0
\]
我们引入的松弛变量\(\mathbf{\hat{x}}\)又称基本变量,\(\mathbf{x}\)又称非基本变量。
单纯型算法
一个例子
为便于理解和描述,我们通过一个例子来讲解迭代过程:
\hat{x}_1 = 30 - x_1 - x_2 - 3x_3 \\
\hat{x}_2 = 24 - 2x_1 - 2x_2 - 5x_3 \\
\hat{x}_3 = 36 - 4x_1 -x_2 - 2x_3
\]
划为矩阵表示为:
1 & 3 & 1 & 2 & 0 & 0 & 0 \\
0 & 1 & 1 & 3 & 1 & 0 & 0 \\
0 & 2 & 2 & 5 & 0 & 1 & 0 \\
0 & 4 & 1 & 2 & 0 & 0 & 1
\end{bmatrix}
\begin{bmatrix}
-z \\
x_1 \\
x_2 \\
x_3 \\
\hat{x}_1 \\
\hat{x}_2 \\
\hat{x}_3
\end{bmatrix} =
\begin{bmatrix}
0 \\
30 \\
24 \\
36
\end{bmatrix}
\]
令$\mathbf{y}=[x_1, x_2, x_3, \hat{x_1}, \hat{x_2}, \hat{x_3}]^T, \mathbf{d}=[\mathbf{c}, 0]^T, z = \mathbf{d^Ty}, \mathbf{\hat{A}=[A, I]} $,则有:
1 & \mathbf{d^T} \\
\mathbf{0} & \hat{A}
\end{bmatrix}
\begin{bmatrix}
z \\
\mathbf{y}
\end{bmatrix} =
\begin{bmatrix}
0\\
\mathbf{b}
\end{bmatrix}
\]
为方便求解\(z\)的最大值,我们可以设计如下的增广矩阵[2],通过对增广矩阵的迭代计算可以得到\(z\)的最大值:迭代结束时增广矩阵右上角的值的相反数。
\begin{array}{c|c}
\mathbf{d^T} & 0 \\
\mathbf{\hat{A}} & \mathbf{b}
\end{array}
\right] =
\left[
\begin{array}{cccccc|c}
3 & 1 & 2 & 0 & 0 & 0 & 0\\
1 & 1 & 3 & 1 & 0 & 0 & 30\\
2 & 2 & 5 & 0 & 1 & 0 & 24\\
4 & 1 & 2 & 0 & 0 & 1 & 36
\end{array}
\right]
\]
下面开始对增广矩阵进行迭代:
原线性规划问题的一个初始解是\(\mathbf{x=0}, \mathbf{\hat{x}=b}\),即\(\mathbf{y_0} = [0, 0, 0, 30, 24, 36]^T\),初始\(\mathbf{d_0}=[3, 1, 2, 0, 0, 0]^T\),\(z=\mathbf{d_0^T}\mathbf{y_0}=0\)
由\(\mathbf{d}\)可知,\(y_0\)的收益最大,因此选择增大\(y_0\)以获取更大收益。判断依据是\(max(\mathbf{d}) == d_0\),并且\(d_0 = 3 \gt 0\)
下面判断\(y_0\)最大可以是多少。取\(\mathbf{b} ./ \hat{A}[:,0] = [30, 12, 9]\)中的最小正整数,即\(y_0 = 9\)
依据高斯消元法[3],将增广矩阵第4行作为基础向量,将第4行作为基础向量的依据是\(\mathbf{b} ./ \hat{A}[:,0]\)的最小值就在增广矩阵的第4行。将增广矩阵中其他行的\(y_0\)的系数化为0,结果为
\begin{array}{c|c}
\mathbf{d^T} & 0 \\
\mathbf{\hat{A}} & \mathbf{b}
\end{array}
\right] =
\left[
\begin{array}{cccccc|c}
0 & 0.25 & 0.5 & 0 & 0 & -0.75 & -27 \\
0 & 0.75 & 2.5 & 1 & 0 & -0.25 & 21\\
0 & 1.5 & 4 & 0 & 1 & -0.5 & 6\\
1 & 0.25 & 0.5 & 0 & 0 & 0.25 & 9
\end{array}
\right]
\]
下面开始新一轮的迭代过程,\(max(\mathbf{d}) == d_2\),并且\(d_2 = 0.5 \gt 0\),因此选择增大\(y_2\)
取\(\mathbf{b} ./ \hat{A}[:,2] = [8.4, 1.5, 18]\)中的最小正整数,即\(y_2 = 1.5\)
取增广矩阵的第3行作为基本向量,对增广矩阵运用高斯消元法将\(y_2\)的其他行的系数划为0得
\begin{array}{c|c}
\mathbf{d^T} & 0 \\
\mathbf{\hat{A}} & \mathbf{b}
\end{array}
\right] =
\left[
\begin{array}{cccccc|c}
0. & 0.0625 & 0. & 0. & -0.125 & -0.6875 &-27.75\\
0. & -0.1875 & 0. & 1 & -0.625 & 0.0625 & 17.25 \\
0. & 0.375 & 1 & 0 & 0.25 & -0.125 & 1.5\\
1 & 0.0625 & 0 & 0 & -0.125 & 0.3125 & 8.25
\end{array}
\right]
\]
下面开始新一轮的迭代过程,\(max(\mathbf{d}) == d_1\),并且\(d_1 = 0.0625 \gt 0\),因此选择增大\(y_1\)
取\(\mathbf{b} ./ \hat{A}[:,1] = [-92, 4, 132]\)中的最小正整数,即\(y_1 = 4\)
取增广矩阵的第3行作为基本向量,对增广矩阵运用高斯消元法得
\begin{array}{c|c}
\mathbf{d^T} & 0 \\
\mathbf{\hat{A}} & \mathbf{b}
\end{array}
\right] =
\left[
\begin{array}{cccccc|c}
0 & 0 & -0.16666667 & 0 & -0.16666667 & -0.66666667 & -28\\
0 & 0 & 0.5 & 1 & -0.5 & 0 & 18\\
0 & 1 & 2.66666667 & 0 & 0.66666667 & -0.33333333 & 4\\
1 & 0 & -0.16666667 & 0 & -0.16666667 & 0.33333333 & 8
\end{array}
\right]
\]
- \(max(\mathbf{d}) == d_1\),并且\(d_0 = 0\),因此迭代结束。最大值\(z=28\)(增广矩阵右上角的值的相反数)
到目前为止,我们已经求得了标准型问题中\(z\)的最大值,但是还没有给出一个解。我们仅仅知道如何求出\(z\)的最大值,但是什么样的\(\mathbf{x}\)会使得\(z\)取得最大值呢?这比知道\(z\)的最大值更重要。
现在观察一下我们已知的一些信息,已知\(z=28\),已知\(y_1 = 4\)。在迭代过程中我们似乎也求得了\(y_0 = 9\)和\(y_2=1.5\),但是实际上这是不对的。因为只有最后一次迭代的结果是准确的,而在迭代过程中得到的只是中间结果,因此我们只知道\(z=28, y_1 = 4\)。另外还有增广矩阵。在本文开头我们有公式:
1 & \mathbf{c^T} & \mathbf{0} \\
\mathbf{0} & \mathbf{A} & \mathbf{I}
\end{bmatrix}
\begin{bmatrix}
-z \\
\mathbf{x} \\
\mathbf{\hat{x}}
\end{bmatrix} =
\begin{bmatrix}
\mathbf{0} \\
\mathbf{b}
\end{bmatrix}
\]
现在我们将已知的值带入上述公式,得到:
1 & 3 & 1 & 2 & 0 & 0 & 0 \\
0 & 1 & 1 & 3 & 1 & 0 & 0 \\
0 & 2 & 2 & 5 & 0 & 1 & 0 \\
0 & 4 & 1 & 2 & 0 & 0 & 1
\end{bmatrix}
\begin{bmatrix}
-z=-28 \\
x_1\\
x_2=4\\
x_3\\
\hat{x}_1\\
\hat{x}_2\\
\hat{x}_3
\end{bmatrix} =
\begin{bmatrix}
0\\
30\\
24\\
36
\end{bmatrix}\\
\mathbf{x} \ge \mathbf{0}, \mathbf{\hat{x}} \ge \mathbf{0}
\]
通过解方程(本文不涉及如何解方程)可以得到一个可行解为:
\]
又已知原始\(\mathbf{c} = [3, 1, 2]^T\),得\(z = \mathbf{c^T} \mathbf{x} = 28\)。
算法过程
问题描述
为了防止读者忘记我们要解决的问题,这里再啰嗦一下,我们要解决的是线性规划问题,并将所有的线性规划问题都归约到标准型上。因此最终问题变成了对标准型的求解。在上文中我们已经通过了一个例子来介绍如何单纯形算法的演算过程,并如何通过迭代的结果求得一个解。下面我们来将这个过程用算法的形式表示出来,但是这个算法仅包含迭代过程,至于如何通过迭代出来的结果求得解,则不是本文关心的内容。
算法的输入与输出
1 & \mathbf{c^T} & \mathbf{0} \\
\mathbf{0} & \mathbf{A} & \mathbf{I}
\end{bmatrix}
\begin{bmatrix}
-z \\
\mathbf{x} \\
\mathbf{\hat{x}}
\end{bmatrix} =
\begin{bmatrix}
\mathbf{0} \\
\mathbf{b}
\end{bmatrix}
\]
这里我们来搞清楚算法的输入和输出。我们在问题中已知的是\(\mathbf{c^T}\)和矩阵\(\mathbf{A}\),以及\(\mathbf{b}\)。因此这些已知值就是算法的输入。而算法的输出则是迭代的最后结果\(z\)的值和\((i, x_i)\)。\((i, x_i)\)是一个元组,其中\(i\)是\(\mathbf{x}\)中的第\(i\)个元素,而\(x_i\)是\(\mathbf{x}\)中的第\(i\)个元素的值(下标从0开始索引)。
算法python实现
python的代码可以当作伪码去阅读,这里直接给出python的实现过程。
def solve(c, A, b):
NUM_NON_BASIC_VARIABLES = c.shape[0]
NUM_BASIC_VARIABLES = b.shape[0]
# z = -d[-1]
d = np.hstack((c, np.zeros(NUM_BASIC_VARIABLES + 1)))
# 初始化增广矩阵
_A = np.hstack((A, np.identity(NUM_BASIC_VARIABLES)))
A_hat = np.c_[_A, b]
_step = 0
last_update_x_inx = -1
last_update_x = 0
while True:
i = np.nanargmax(d[:-1])
if d[i] <= 0:
break
# 利用高斯消元法求解
_res = A_hat[:, -1] / A_hat[:, i]
# 将忽略 divided by zero 的结果,系数小于等于0的也不能考虑在内
j = np.where(_res > 0, _res, np.inf).argmin()
if _res[j] <= 0: # 系数小于等于0的会违反了 >= 0 的基本约束条件
break
last_update_x_inx = i
last_update_x = _res[j]
# 下面计算y中除了y[i]之外的值
# 1.运用高斯消元法
A_hat[j, :] = A_hat[j, :] / A_hat[j, i] # A_hat[j,i] = 1
# for _row in range(A_hat.shape[0]):
# if _row != j:
# A_hat[_row,:] = A_hat[_row,:] - A_hat[_row,i] * A_hat[j,:]
# 下面四行等价于上述的for循环
_tmp = np.copy(A_hat[j, :])
_A = np.outer(A_hat[:, i], _tmp) # 列向量乘以行向量
A_hat -= _A
A_hat[j, :] = _tmp
d = d - d[i] * A_hat[j, :]
# 打印中间过程
_step += 1
# print('step:', _step)
# print('d = ', d)
# print('A_hat = ', A_hat)
# print('z = ', -d[-1])
z = -d[-1]
if last_update_x_inx == -1:
return None
return (z, (last_update_x_inx, last_update_x)) # return z
拓展阅读
参考文献
线性规划之单纯形算法矩阵描述与python实现的更多相关文章
- LP线性规划求解 之 单纯形 算法
LP线性规划求解 之 单纯形 算法 认识-单纯形 核心: 顶点旋转 随机找到一个初始的基本可行解 不断沿着可行域旋转(pivot) 重复2,直到结果不能改进为止 案例-过程 以上篇的case2的松弛型 ...
- 模拟退火算法SA原理及python、java、php、c++语言代码实现TSP旅行商问题,智能优化算法,随机寻优算法,全局最短路径
模拟退火算法SA原理及python.java.php.c++语言代码实现TSP旅行商问题,智能优化算法,随机寻优算法,全局最短路径 模拟退火算法(Simulated Annealing,SA)最早的思 ...
- 机器学习经典算法详解及Python实现--基于SMO的SVM分类器
原文:http://blog.csdn.net/suipingsp/article/details/41645779 支持向量机基本上是最好的有监督学习算法,因其英文名为support vector ...
- 机器学习经典算法具体解释及Python实现--线性回归(Linear Regression)算法
(一)认识回归 回归是统计学中最有力的工具之中的一个. 机器学习监督学习算法分为分类算法和回归算法两种,事实上就是依据类别标签分布类型为离散型.连续性而定义的. 顾名思义.分类算法用于离散型分布预測, ...
- 机器学习经典算法具体解释及Python实现--K近邻(KNN)算法
(一)KNN依旧是一种监督学习算法 KNN(K Nearest Neighbors,K近邻 )算法是机器学习全部算法中理论最简单.最好理解的.KNN是一种基于实例的学习,通过计算新数据与训练数据特征值 ...
- 数据结构与算法C++描述学习笔记1、辗转相除——欧几里得算法
前面学了一个星期的C++,以前阅读C++代码有些困难,现在好一些了.做了一些NOI的题目,这也是一个长期的目标中的一环.做到动态规划的相关题目时发现很多问题思考不通透,所以开始系统学习.学习的第一本是 ...
- 《数据结构与算法JavaScript描述》
<数据结构与算法JavaScript描述> 基本信息 作者: (美)Michael McMillan 译者: 王群锋 杜欢 丛书名: 图灵程序设计丛书 出版社:人民邮电出版社 ISBN:9 ...
- 翻阅《数据结构与算法javascript描述》--数组篇
导读: 这篇文章比较长,介绍了数组常见的操作方法以及一些注意事项,最后还有几道经典的练习题(面试题). 数组的定义: JavaScript 中的数组是一种特殊的对象,用来表示偏移量的索引是该对象的属性 ...
- 数据结构与算法javascript描述
<数据结构与算法javascript描述>--数组篇 导读: 这篇文章比较长,介绍了数组常见的操作方法以及一些注意事项,最后还有几道经典的练习题(面试题). 数组的定义: JavaScri ...
随机推荐
- scheduler源码分析——调度流程
前言 当api-server处理完一个pod的创建请求后,此时可以通过kubectl把pod get出来,但是pod的状态是Pending.在这个Pod能运行在节点上之前,它还需要经过schedule ...
- 简易集成websocket技术实现消息推送
Websocket 简介 首先介绍下WebSocket,它是一种网络通信技术,该技术最大的特点就是,服务器端可以主动往客户端发送消息:当然,客户端也可以主动往服务器发送消息,实现两端的消息通信,属于网 ...
- mybatis 配置问题查找
mybatis配置问题记录 org.apache.ibatis.binding.BindingException: Type interface com.xx.dao.UserDao(自己项目的文件) ...
- 迁移appseting.json创建自定义配置中心
创建一个自定义的配置中心,将框架中各类配置,迁移至数据库,支持切换数据库,热重载. 说在前面的话 自使用.net Core框架以来,配置大多存在json文件中: [框架默认加载配置]文件为appset ...
- FastAPI 学习之路(十一)请求体 - 嵌套模型
系列文章: FastAPI 学习之路(一)fastapi--高性能web开发框架 FastAPI 学习之路(二) FastAPI 学习之路(三) FastAPI 学习之路(四) FastAPI 学习之 ...
- Mybatis、maven项目中整合log4j (17)
Mybatis.maven项目总整合log4j java 中Mybatis.maven项目总整合log4j 1.pom增加log4j包引用 2.添加 log4j.properties文件 # java ...
- C语言知识_1
+,-,*,/是C语言中表示四则运算的符号.:用来分割不同的语句{}用来对语句进行分组 函数代表了一组数据处理过程,由一对大括号所包含的多条语句来表示这个处理过程.每个函数有唯一的名字,main函数是 ...
- 【c++ Prime 学习笔记】第3章 字符串、向量和数组
string和vector是两类最重要的标准库类型 strng表示可变长的字符序列 vector存放某种给定类型对象的可变长序列. 3.1 命名空间的using声明 using namespace:: ...
- 什么是产品待办列表?(What is Product Backlog)
正如scrum指南中所描述的,产品待办事项列表是一个紧急而有序的列表,其中列出了改进产品所需的内容.它是scrum团队承担的工作的唯一来源. 在sprint计划 (Sprint Planning)活动 ...
- 基于JWT的Token身份验证
身份验证,是指通过一定的手段,完成对用户身份的确认.为了及时的识别发送请求的用户身份,我们调研了常见的几种认证方式,cookie.session和token. 1.Cookie cookie是 ...