线性规划之单纯形算法矩阵描述与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 ...
随机推荐
- mysql从零开始之MySQL 选择数据库
MySQL 选择数据库 在你连接到 MySQL 数据库后,可能有多个可以操作的数据库,所以你需要选择你要操作的数据库. 从命令提示窗口中选择MySQL数据库 在 mysql> 提示窗口中可以很简 ...
- 细说JUC的线程池架构
前言 线程的创建是需要JVM和OS(操作系统)相互配合的,一次的创建要花费许多的资源. 1.首先,JVM要为该线程分配堆栈和初始化大量内存块,栈内存至少是1MB. 2.其次便是要进行系统的调用,在OS ...
- node二进制安装
你可能因为重装系统node的npm不管用, 但是node管用, 我不知道为什么, 但是 二进制安装就好了 管他那么多 下面这些网址你就可以了 https://blog.csdn.net/wocaoni ...
- 洛谷4051 JSOI2007 字符加密(SA)
真是一道良好的SA模板题 首先,由于涉及到从左边移动到右边这个过程,我们不妨直接把字符串复制一遍,接在后面. 然后直接构造后缀数组,按排名从小到大,枚举所有的位置,如果这个后缀的起始点是在原串中的,那 ...
- 洛谷3244 落忆枫音 (拓扑图dp+式子)
题目大意就是 给你一个DAG 然后添加一条边\(x->y\) ,询问以1为根的生成树的个数 QWQ 首先假设没有添加的边 答案就应该是 \[ans=\prod_{i=1}^{n} in[i] \ ...
- 重学c#系列——字典(十一)
前言 重学c#系列继续更新,简单看一下字典的源码. 看源码主要是解释一下江湖中的两个传言: 字典foreach 顺序是字典添加的顺序 字典删除元素后,字典顺序将会改变 正文 那么就从实例化开始看起,这 ...
- Upload-labs通关指南(上) 1-10
Upload-labs 所有文章和随笔(随笔将不于csdn更新)将于我的个人博客(移动端暂未适配)第一时间更新. 一些网站存在文件上传接口,一旦存在这个接口就有可能存在漏洞. 文件上传漏洞的逻辑是将一 ...
- VS2019中安装2017,2015
VS2019中安装2017,2015
- vue3 element-plus 配置json快速生成form表单组件,提升生产力近600%(已在公司使用,持续优化中)
️本文为博客园社区首发文章,未获授权禁止转载 大家好,我是aehyok,一个住在深圳城市的佛系码农♀️,如果你喜欢我的文章,可以通过点赞帮我聚集灵力️. 个人github仓库地址: https:gi ...
- 【UE4 C++】DateTime、Timespan 相关函数
基于UKismetMathLibrary DateTime 相关函数 Timespan 运算操作相关函数见尾部附录 /** Returns the date component of A */ UFU ...