单纯形法原理
单纯形法#
参考连接:单纯形法
单纯形法是针对求解线性规划问题的一个算法,这个名称里的 “单纯形” 是代数拓扑里的一个概念,可以简单将“单纯形”理解为一个凸集,标准的线性规划问题(线性规划标准型)可以表示为:
单纯形法最早由 George Dantzig于1947年提出,单纯形法对于求解线性规划问题是具有跨时代意义的,其实不仅仅是针对线性规划,非线性规划问题在求解的过程中也大量依赖单纯形法。
一、凸集及其性质的介绍#
1.1 凸集的概念#
单纯形可以理解为一个凸集,介绍单纯形法之前有必要先来了解一下凸集概念及其性质,凸集上求最优解是凸优化的一个分支,凸集对于简化问题是有重要意义的。
凸集可以用图形表示为一个没有洞的联通区域,如下图所示:
凸集的概念:
-
(1)如果集合
中任意两个点 ,其连线上所有的点也都是集合 中的点,称 为凸集 -
(2)
的连线可以表示为: -
(3)凸集定义用数学解析式可表示为:对
,有:
关于(2),以一个一维坐标例子简单理解一下。如图,
根据定义下面这个图形所定义的区域不是凸集:
1.2 凸集的顶点#
顶点:
凸集
-
如果
中不存在任何两个不同的点 ,使 成为这两个点连线上的一个点。 -
或者这样表述:对
,不存在 ,则称 是凸集 的顶点
如图,因为
1.3 几个基本定理#
定理1 若线性规划问题存在可行解,则问题的可行域是凸集
- 证明见《运筹学教程(第五版)》第20页
定理2 线性规划问题的基可行解
定理3 若线性规划问题有最优解,一定存在一个基可行解是最优解
二、单纯形法原理#
2.1 线性规划问题的解的概念#
-
可行解 满足所有约束条件的解,称为线性规划问题的可行解。全部可行解的集合称为可行域。
-
最优解 使目标函数达到最大值的可行解称为最优解。
-
基 设
为约束方程组的 阶系数矩阵,其秩为 , 是 的一个 阶满秩子矩阵,称 是线性规划问题的一个基。-
中每一个列向量 称为基向量 -
与基向量
对应的变量 称为基变量 -
线性规划中除基变量以外的变量称为非基变量
-
-
基解 由
个约束方程可解出 个基变量的唯一解 ,将这个解加上非基变量取 的值产生的解 ,称为线性规划问题的基解。- 基解中变量取非
值的个数不大于方程数 ,故基解的总数不超过 个
- 基解中变量取非
-
基可行解 满足变量非负约束条件(如
)的基解称为基可行解- 单纯型法应用于标准化形式的线性规划问题,所以在线性规划问题中决策变量
均大于等于
- 单纯型法应用于标准化形式的线性规划问题,所以在线性规划问题中决策变量
-
可行基 对应于基可行解的基称为可行基
2.2 单纯形法的实现#
根据定理2和定理3,最优解一定是一个基可行解,且为凸集的一个顶点。
单纯形法的核心思想可以归纳为: 找到每一个基本可行解,代入目标函数后计算函数值取其最大或最小值即可。单纯形法上从代数角度是寻找约束条件的每一个基本可行解,从几何意义上来说是遍历凸集的每一个顶点,根据算法的特性有时也称为转轴法。
当求解最小值问题时,如果借助梯度的概念,在得到一个顶点后,切换下一个顶点方向是函数变小的方向无疑会节省很多时间。
2.2.1 单纯形法的迭代#
约束条件系数矩阵
中,将作为基的列集合记作 (或者说 为线性规划问题的一个基);非基的列集合记作 ,则有: 。其中, 均为 的一个分块矩阵。
同样地,把
分量中基变量的的集合记作 ,非基变量集合记为
约束条件可写为:
当
同样地,将目标函数中的系数向量
也分为两部分,基变量对应的系数向量记为 ,非基变量对应的系数记为
由此,可得到目标函数的另一种表达形式:
把(1)代入(2)中,得到目标函数另一种表达形式:
若此时存在初始基可行解,即
对(3)进行求导,可得:
若
如何选择入基呢?函数的微分形式有:
- 补充说明:
,所以: 。又因为: ,所以
由(5)可得,
所以,
入基已经准备就绪,那么如何选择出基呢?即如何选择被替代的基列?在选择入基是其实还有一个隐含的问题,入基被选择后,新的基变量值是多少?这些可以从公式(1)中得到答案:
这里设
由于约束条件
故此,当
得到
2.2.3 单纯形法代码#
import numpy as np
BASEINDEX=2
#列变换实现单纯形法
def simplex_ColTranslate(c ,A,b,flagstable):
B=A[:,BASEINDEX:]
Binv=np.linalg.inv(B)
xb=np.dot(Binv ,b)
cb=c[:,BASEINDEX:]
f=np.dot(cb,xb )
Inbaseindex=-1
OutBaseIndex = -1
tempvalue=0
for i in range(BASEINDEX):
v=np.squeeze(np.dot(np.dot(cb,Binv),A[:,i])-c[:,i],0)
if tempvalue<=v:
#找出入基
tempvalue=v
Inbaseindex=i
if tempvalue==0:
#找到最优解
return xb,f,c,flagstable
else:
#入基出基替换,同时交换系数c
y=np.dot(Binv,A[:,Inbaseindex])
minivalue=None
for i in range(y.shape[0]):
if y[i]>0:
x=xb[i]/y[ i]
#找出最小值,保证xb>=0
if minivalue is None or minivalue>x :
minivalue=x
OutBaseIndex=i+BASEINDEX
for i in range(A.shape[1]):
if i==OutBaseIndex:
for j in range(A.shape[0]):
tmp=A[j,i].copy()
A[j,i]=A[j, Inbaseindex]
A[j, Inbaseindex]=tmp
tempc= np.squeeze( c[:,OutBaseIndex],0).copy()
c[:,OutBaseIndex]=c[:,Inbaseindex]
c[:,Inbaseindex]=tempc
flagstable[OutBaseIndex-BASEINDEX]=Inbaseindex
return simplex_ColTranslate(c, A, b,flagstable)
def useColTranslate():
BASEINDEX = 2
c = np.array([[-4, -1, 0, 0, 0]],dtype=np.float)
A = np.array([[-1, 2, 1, 0, 0], [2, 3, 0, 1, 0], [1, -1, 0, 0, 1]],dtype=np.float)
b = np.array([[4], [12], [3]],dtype=np.float)
# 记录下标变动情况
flagstable = {}
for i in range(c.shape[1] - BASEINDEX):
flagstable[i] = BASEINDEX + i
x, f, c, flags = simplex_ColTranslate(c, A, b, flagstable)
print('最优解:%.2f' % (f[0, 0]))
for j in range(x.shape[0]):
print('x%d=%.1f' % (flags[j] + 1, x[j, 0]))
if __name__ == '__main__':
useColTranslate()
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具