线性规划之单纯形算法矩阵描述与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 ...
随机推荐
- Redis三种集群模式介绍
三种集群模式 redis有三种集群模式,其中主从是最常见的模式. Sentinel 哨兵模式是为了弥补主从复制集群中主机宕机后,主备切换的复杂性而演变出来的.哨兵顾名思义,就是用来监控的,主要作用就是 ...
- Pandas 数据的一些基本操作
一个很偶然的机会,主动出击挑战一个之前没有尝试过的新东西,在做的过程中需要处理一些csv文件的数据,以下是我总结的一些小方法,希望对和我一样的新手朋友们有所帮助,初次尝试,望路过的朋友有更好的方法可以 ...
- SpringBoot-thymeleaf-静态资源引入和接管
引入前端 templates下放html页面 static下放css.js.image等静态资源 添加thymeleaf命名空间 <html lang="en" xmlns: ...
- Windows用cmd编译运行C程序
在Windows环境下用命令行编译运行程序 浙江大学-C语言程序设计进阶 配置gcc 准备一个Dev-cpp 找到gcc.exe所在目录 Dev-Cpp\MinGW64\bin 地址栏右键将地址复制为 ...
- Boost Started on Unix Variants
- IEEE754浮点数的转换
将十进制数转换为单精度浮点数 如何将十进制数转换为单精度浮点数参考 首先要知道 IEEE浮点标准:V=(-1)^s * M * 2^E 1.符号(sign)s决定这个数是负数(s=1)还是正数,0(s ...
- WPF中的命令(Command)
这节来讲一下WPF中的命令(Command)的使用. [认识Command] 我们之前说过,WPF本身就为我们提供了一个基础的MVVM框架,本节要讲的命令就是其中一环,通过在ViewModel中声明命 ...
- CentOS 文本编辑器
目录 1.Nano 1.1.基础命令 1.2.快捷操作 1.3.配置文件 2.Vim 2.1.四大模式 2.2.基础命令 2.3.标准操作 2.4.高级操作 2.5.配置文件 Linux 终端的文本编 ...
- 【UE4 C++】 外部图片读取、Texture 保存 png
蓝图版 导入外部图片 file://E:/UE___Projects_Test/MyProjectAAA/Plugins/WXimage.jpg 导出图图片 一般导出 .hdr 文件 导出 png 设 ...
- [软软软]技术博客-Commitizen优化git commit
工具介绍 commitizen/cz-cli是一个规范git commit的工具,使用它代替git commit能够方便有效地写好提交的log,使得团队项目的版本信息更清晰. 安装 (全局安装) np ...