声明

本文为本人原创,转载请注明出处。本文仅发表在博客园,作者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. 安装SpaCy出现报错:requests.exceptions.ConnectionError: HTTPSConnectionPool(host='raw.githubusercontent.com', port=443):

    内含安装步骤及报错解决:https://www.cnblogs.com/xiaolan-Lin/p/13286885.html

  2. HTML模板标签解析

    HTML基本模板 1 <!DOCTYPE html> 2 <html lang="zh-CN"> 3 <head> 4 <meta cha ...

  3. RobotFramework+Selenium如何提高脚本稳定性

     通过RF来跑selenium的脚本,正常运行一遍都没有问题,但如果要多次运行,提高脚本的稳定性,那么应该如何做呢?  当然有时候最简单最简单的方法就是直接通过sleep来等待,虽然简单粗暴,但会带来 ...

  4. 题解 CF833D Red-Black Cobweb

    题目传送门 题目大意 给出一个 \(n\) 个点的树,每条边有边权和颜色 \(0,1\) ,定义一条链合法当且仅当 \(0,1\) 颜色的边数之比小于等于 \(2\) ,求所有合法的链的边权之积的积. ...

  5. 利用ps在光污染地图上寻找最近的观星地区

    城市灯光对于天文观测和天文摄影是有害的,进行这两类活动之前应提前规划地点,下面是笔者尝试的一种利用ps在光污染地图上进行规划的方法. 目前大部分的光污染地图都是基于WA 2015绘制的,可以结合VII ...

  6. ❤️【Python从入门到精通】(二十七)更进一步的了解Pillow吧!

    您好,我是码农飞哥,感谢您阅读本文,欢迎一键三连哦. 进一步介绍Pillow库的使用,详细了解 干货满满,建议收藏,需要用到时常看看. 小伙伴们如有问题及需要,欢迎踊跃留言哦~ ~ ~. 前言 本文是 ...

  7. Java(36)IO流案例与总结

    作者:季沐测试笔记 原文地址:https://www.cnblogs.com/testero/p/15228455.html 博客主页:https://www.cnblogs.com/testero ...

  8. Flask的环境配置

      Flask django是大而全,提供所有常用的功能 flask是小而精,只提供核心功能 环境配置 为了防止 django和 flask环境相互冲突,可以使用 虚拟环境分割开 pip instal ...

  9. Coursera Deep Learning笔记 序列模型(二)NLP & Word Embeddings(自然语言处理与词嵌入)

    参考 1. Word Representation 之前介绍用词汇表表示单词,使用one-hot 向量表示词,缺点:它使每个词孤立起来,使得算法对相关词的泛化能力不强. 从上图可以看出相似的单词分布距 ...

  10. 合理占用服务器空闲GPU[狗头]

    合理占用服务器GPU资源[狗头] 场景:当你想进行模型训练时,发现GPU全被占用,怎么办? 解决方案1: 在终端输入如下命令:watch -n 设定刷新时间(s) nvidia-smi 然后记起来了回 ...