声明

本文为本人原创,转载请注明出处。本文仅发表在博客园,作者LightningStar。

问题描述

所有的线性规划问题都可以归约到标准型的问题,规约过程比较简单且已经超出本文范围,不再描述,可以参考拓展阅读部分。下面直接给出线性规划标准型描述。

标准型描述

线性规划问题标准型的矩阵描述[1]

目标:

\[maximize \quad z = \mathbf{c^Tx}
\]

约束:

\[\mathbf{Ax} \leq \mathbf{b} \\
\mathbf{x} \geq \mathbf{0}
\]

我们的最终目标在约束条件下就是找到一个解\(\mathbf{x}\),使得\(z\)最大。注意我们的描述,我们的解是找到一组值\(\mathbf{x}\)使得\(z\)最大,这组值才是问题的一个解,\(z\)得最大值究竟是多少并不是问题的解。

在后文中,粗体小写字母一般表示向量,粗体大写字母一般表示矩阵,小写字母表示标量。

松弛型

在用单纯型法求解线性规划问题之前,必须先把线性规划问题转换成增广矩阵形式。增广矩阵形式引入非负松弛变量将不等式约束变成等式约束。

引入松弛变量\(\mathbf{\hat{x}} = \mathbf{b} - \mathbf{Ax}\),则有:

\[\begin{align}
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{equation}
\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}\)又称非基本变量。

单纯型算法

一个例子

为便于理解和描述,我们通过一个例子来讲解迭代过程:

\[z = 3x_1 + x_2 + 2x_3 \\
\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
\]

划为矩阵表示为:

\[\begin{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 \\
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]} $,则有:

\[\begin{bmatrix}
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\)的最大值:迭代结束时增广矩阵右上角的值的相反数。

\[\left[
\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]
\]

下面开始对增广矩阵进行迭代:

  1. 原线性规划问题的一个初始解是\(\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\)

  2. 由\(\mathbf{d}\)可知,\(y_0\)的收益最大,因此选择增大\(y_0\)以获取更大收益。判断依据是\(max(\mathbf{d}) == d_0\),并且\(d_0 = 3 \gt 0\)

  3. 下面判断\(y_0\)最大可以是多少。取\(\mathbf{b} ./ \hat{A}[:,0] = [30, 12, 9]\)中的最小正整数,即\(y_0 = 9\)

  4. 依据高斯消元法[3],将增广矩阵第4行作为基础向量,将第4行作为基础向量的依据是\(\mathbf{b} ./ \hat{A}[:,0]\)的最小值就在增广矩阵的第4行。将增广矩阵中其他行的\(y_0\)的系数化为0,结果为

\[\left[
\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]
\]
  1. 下面开始新一轮的迭代过程,\(max(\mathbf{d}) == d_2\),并且\(d_2 = 0.5 \gt 0\),因此选择增大\(y_2\)

  2. 取\(\mathbf{b} ./ \hat{A}[:,2] = [8.4, 1.5, 18]\)中的最小正整数,即\(y_2 = 1.5\)

  3. 取增广矩阵的第3行作为基本向量,对增广矩阵运用高斯消元法将\(y_2\)的其他行的系数划为0得

\[\left[
\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]
\]
  1. 下面开始新一轮的迭代过程,\(max(\mathbf{d}) == d_1\),并且\(d_1 = 0.0625 \gt 0\),因此选择增大\(y_1\)

  2. 取\(\mathbf{b} ./ \hat{A}[:,1] = [-92, 4, 132]\)中的最小正整数,即\(y_1 = 4\)

  3. 取增广矩阵的第3行作为基本向量,对增广矩阵运用高斯消元法得

\[\left[
\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]
\]
  1. \(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\)。另外还有增广矩阵。在本文开头我们有公式:

\[\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}
\]

现在我们将已知的值带入上述公式,得到:

\[\begin{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{x} = [8, 4, 0]^T, \mathbf{\hat{x}}=[8, 0, 0]^T
\]

又已知原始\(\mathbf{c} = [3, 1, 2]^T\),得\(z = \mathbf{c^T} \mathbf{x} = 28\)。

算法过程

问题描述

为了防止读者忘记我们要解决的问题,这里再啰嗦一下,我们要解决的是线性规划问题,并将所有的线性规划问题都归约到标准型上。因此最终问题变成了对标准型的求解。在上文中我们已经通过了一个例子来介绍如何单纯形算法的演算过程,并如何通过迭代的结果求得一个解。下面我们来将这个过程用算法的形式表示出来,但是这个算法仅包含迭代过程,至于如何通过迭代出来的结果求得解,则不是本文关心的内容。

算法的输入与输出

\[\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}
\]

这里我们来搞清楚算法的输入和输出。我们在问题中已知的是\(\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

拓展阅读

单纯形算法

参考文献


  1. 线性规划--维基百科

  2. 增广矩阵--维基百科

  3. 高斯消元法--维基百科

线性规划之单纯形算法矩阵描述与python实现的更多相关文章

  1. LP线性规划求解 之 单纯形 算法

    LP线性规划求解 之 单纯形 算法 认识-单纯形 核心: 顶点旋转 随机找到一个初始的基本可行解 不断沿着可行域旋转(pivot) 重复2,直到结果不能改进为止 案例-过程 以上篇的case2的松弛型 ...

  2. 模拟退火算法SA原理及python、java、php、c++语言代码实现TSP旅行商问题,智能优化算法,随机寻优算法,全局最短路径

    模拟退火算法SA原理及python.java.php.c++语言代码实现TSP旅行商问题,智能优化算法,随机寻优算法,全局最短路径 模拟退火算法(Simulated Annealing,SA)最早的思 ...

  3. 机器学习经典算法详解及Python实现--基于SMO的SVM分类器

    原文:http://blog.csdn.net/suipingsp/article/details/41645779 支持向量机基本上是最好的有监督学习算法,因其英文名为support vector  ...

  4. 机器学习经典算法具体解释及Python实现--线性回归(Linear Regression)算法

    (一)认识回归 回归是统计学中最有力的工具之中的一个. 机器学习监督学习算法分为分类算法和回归算法两种,事实上就是依据类别标签分布类型为离散型.连续性而定义的. 顾名思义.分类算法用于离散型分布预測, ...

  5. 机器学习经典算法具体解释及Python实现--K近邻(KNN)算法

    (一)KNN依旧是一种监督学习算法 KNN(K Nearest Neighbors,K近邻 )算法是机器学习全部算法中理论最简单.最好理解的.KNN是一种基于实例的学习,通过计算新数据与训练数据特征值 ...

  6. 数据结构与算法C++描述学习笔记1、辗转相除——欧几里得算法

    前面学了一个星期的C++,以前阅读C++代码有些困难,现在好一些了.做了一些NOI的题目,这也是一个长期的目标中的一环.做到动态规划的相关题目时发现很多问题思考不通透,所以开始系统学习.学习的第一本是 ...

  7. 《数据结构与算法JavaScript描述》

    <数据结构与算法JavaScript描述> 基本信息 作者: (美)Michael McMillan 译者: 王群锋 杜欢 丛书名: 图灵程序设计丛书 出版社:人民邮电出版社 ISBN:9 ...

  8. 翻阅《数据结构与算法javascript描述》--数组篇

    导读: 这篇文章比较长,介绍了数组常见的操作方法以及一些注意事项,最后还有几道经典的练习题(面试题). 数组的定义: JavaScript 中的数组是一种特殊的对象,用来表示偏移量的索引是该对象的属性 ...

  9. 数据结构与算法javascript描述

    <数据结构与算法javascript描述>--数组篇 导读: 这篇文章比较长,介绍了数组常见的操作方法以及一些注意事项,最后还有几道经典的练习题(面试题). 数组的定义: JavaScri ...

随机推荐

  1. Jetbrains CLion 安装与激活 详解

    1. 下载与安装 1.1 下载 这里提供了三个操作系统的官网下载地址 Mac Windows Linux 进入页面后向下拉点击蓝色按钮即可下载. 1.2 安装 这里将用 MacOS 来进行示例,Win ...

  2. 怒肝 Linux 学习路线,这回不难

    Linux 学习路线 by 鱼皮. 原创不易,请勿抄袭,违者必究! 大家好,我是鱼皮,又花 1 周肝出了 Linux 学习资料全家桶,包括学习路线.命令手册.视频.书籍.文档.实战教程.社区.工具.大 ...

  3. 使用node-gyp编写简单的node原生模块

    通过样例,让我们了解如何编写一个node的原生模块.当然,这篇文章还有一个目的,是为了方便以后编写关于node-gyp的文章,搭建初始环境. 基于node-addon-api 基于node-addon ...

  4. 实时获取股票数据,免费!——Python爬虫Sina Stock实战

    更多精彩内容,欢迎关注公众号:数量技术宅,也可添加技术宅个人微信号:sljsz01,与我交流. 实时股票数据的重要性 对于四大可交易资产:股票.期货.期权.数字货币来说,期货.期权.数字货币,可以从交 ...

  5. res目录下的结构

    目录 res目录下的结构 drawable开头的文件夹 mipmap开头的文件夹 values开头的文件夹 layout文件夹 使用res目录下的资源 res目录下的结构 如果你展开res目录看一下, ...

  6. docker中Jenkins启动无法安装插件,版本过低

    一.问题现象: 使用docker启动jenkins,在jenkins启动后却无法安装jenkins的插件,一直提示安装失败且从log看到提示信息显示为需要升级jenkins的版本 二.原因分析: 在使 ...

  7. 【机器学习基础】逻辑回归——LogisticRegression

    LR算法作为一种比较经典的分类算法,在实际应用和面试中经常受到青睐,虽然在理论方面不是特别复杂,但LR所牵涉的知识点还是比较多的,同时与概率生成模型.神经网络都有着一定的联系,本节就针对这一算法及其所 ...

  8. '\r'(回车符),'\n'(换行符)与"\r\n"

    一.'\n','\r'和"\r\n" 回车\r本义是光标重新回到本行开头,r的英文return,控制字符可以写成CR,即Carriage Return(回车,carriage有&q ...

  9. Scrum Meeting 1补充会议

    日期:2021年04月24日 会议主要内容概述: 本次会议于11:30举行,对项目架构做出了重要调整,并根据该调整修改了第1次例会报告中后两日计划完成的工作部分. 一.架构调整 会上讨论了用户模块相关 ...

  10. JavaAgent型内存马基础

    Java Instrumentation ​ java Instrumentation指的是可以用独立于应用程序之外的代理(agent)程序来监测和协助运行在JVM上的应用程序.这种监测和协助包括但不 ...