码头牛牛Blog点我刷新(小声)

单纯形法原理

码头牛牛·2025-01-23 00:02·77 次阅读

单纯形法原理

单纯形法#

参考连接:单纯形法

单纯形法是针对求解线性规划问题的一个算法,这个名称里的 “单纯形” 是代数拓扑里的一个概念,可以简单将“单纯形”理解为一个凸集,标准的线性规划问题(线性规划标准型)可以表示为:

max(ormin)f(x)=cxs.t.Ax=bx0

单纯形法最早由 George Dantzig于1947年提出,单纯形法对于求解线性规划问题是具有跨时代意义的,其实不仅仅是针对线性规划,非线性规划问题在求解的过程中也大量依赖单纯形法。

一、凸集及其性质的介绍#

1.1 凸集的概念#

单纯形可以理解为一个凸集,介绍单纯形法之前有必要先来了解一下凸集概念及其性质,凸集上求最优解是凸优化的一个分支,凸集对于简化问题是有重要意义的。

凸集可以用图形表示为一个没有洞的联通区域,如下图所示:

凸集的概念:

  • (1)如果集合C中任意两个点X1,X2,其连线上所有的点也都是集合C中的点,称C为凸集

  • (2)X1,X2的连线可以表示为:aX1+(1a)X2(0<a<1)

  • (3)凸集定义用数学解析式可表示为:对X1,X2C,有:aX1+(1a)X2C(0<a<1)

关于(2),以一个一维坐标例子简单理解一下。如图,aX1+(1a)X2=a(X1X2)+X2(0<a<1),其中a=0a=1分别代表X2X1a(0,1)代表点X1和点X2连线中任意一点

根据定义下面这个图形所定义的区域不是凸集:

1.2 凸集的顶点#

顶点:

凸集C中满足下述条件的点X称为顶点:

  • 如果C中不存在任何两个不同的点X1,X2,使X成为这两个点连线上的一个点。

  • 或者这样表述:对X1,X2C,不存在X=aX1+(1a)X2(0<a<1),则称X是凸集C的顶点

如图,因为a01,所以X不能成为C中某条连线上的一个点。

1.3 几个基本定理#

定理1    若线性规划问题存在可行解,则问题的可行域是凸集

  • 证明见《运筹学教程(第五版)》第20页

定理2    线性规划问题的基可行解X对应线性规划问题可行域(凸集)的顶点

定理3    若线性规划问题有最优解,一定存在一个基可行解是最优解

二、单纯形法原理#

2.1 线性规划问题的解的概念#

  • 可行解    满足所有约束条件的解,称为线性规划问题的可行解。全部可行解的集合称为可行域

  • 最优解    使目标函数达到最大值的可行解称为最优解。

  •     设A为约束方程组的m×n(m>n)阶系数矩阵,其秩为mBA的一个m×m满秩子矩阵,称B是线性规划问题的一个

    • B中每一个列向量Pj(j=1,,m)称为基向量

    • 与基向量Pj对应的变量xj称为基变量

    • 线性规划中除基变量以外的变量称为非基变量

  • 基解    由m个约束方程可解出m个基变量的唯一解XB,将这个解加上非基变量取0的值产生的解X,称为线性规划问题的基解。

    • 基解中变量取非0值的个数不大于方程数m,故基解的总数不超过Cnm
  • 基可行解    满足变量非负约束条件(如x0)的基解称为基可行解

    • 单纯型法应用于标准化形式的线性规划问题,所以在线性规划问题中决策变量x均大于等于0
  • 可行基    对应于基可行解的基称为可行基

2.2 单纯形法的实现#

根据定理2和定理3,最优解一定是一个基可行解,且为凸集的一个顶点。

单纯形法的核心思想可以归纳为: 找到每一个基本可行解,代入目标函数后计算函数值取其最大或最小值即可。单纯形法上从代数角度是寻找约束条件的每一个基本可行解,从几何意义上来说是遍历凸集的每一个顶点,根据算法的特性有时也称为转轴法。

当求解最小值问题时,如果借助梯度的概念,在得到一个顶点后,切换下一个顶点方向是函数变小的方向无疑会节省很多时间

2.2.1 单纯形法的迭代#

约束条件系数矩阵A中,将作为基的列集合记作B(或者说B为线性规划问题的一个基);非基的列集合记作N,则有:A=(N,B)。其中,NB均为A的一个分块矩阵。

同样地,把x=[x1x2xn]分量中基变量的的集合记作xB,非基变量集合记为xN

约束条件可写为:(N,B)(xN,xB)=b,得到等式NxN+Bxb=b,矩阵B是基矩阵且可逆,得到:

(1)BxB=bNxNB1BxB=B1bB1NxNxB=B1bB1NxN

xN=0时,xB=B1b,若B1b0,则称xB初始基可行解

同样地,将目标函数中的系数向量c也分为两部分,基变量对应的系数向量记为cB,非基变量对应的系数记为cN

由此,可得到目标函数的另一种表达形式:

(2)f(x)=cBxB+cNxN

把(1)代入(2)中,得到目标函数另一种表达形式:

(3)f(x)=cB(B1bB1NxN)+cNxN=cBB1b(cBB1NcN)xN

若此时存在初始基可行解,即xN=0xB=B1b,代入(3)后可得到:

(4)f(x)=cBB1b

对(3)进行求导,可得:

(5)fx=(cBB1NcN)

fx<0,即cBB1NcN>0,说明此时目标函数是单调递减的,也就是在取初始基可行解后目标函数还有变小的空间。此时可以选择矩阵中某个非基变量(非基集合中的某一列)来替代现有基变量(替代现有基),即选择入基。

如何选择入基呢?函数的微分形式有:

(6)f(x+Δx)=f(x)+fxΔx

  • 补充说明:fx=Δf(x)Δx,所以:Δf(x)=fxΔx。又因为:Δf(x)=f(x+Δx)f(x),所以f(x+Δx)=f(x)+Δf(x)=f(x)+fxΔx

由(5)可得,cBB1NcN越大则fx越小,目标函数变小的幅度也就越大。将此作为选择入基的标准,遍历每个非基列pjpjN中的一个列向量),由于每次都只选择一个列作为入基,xN其他分量为0

所以,cBB1NcN简化为cBB1pjcj,引入每一个待选列检验量σj=cBB1pjcj如果检验量大于0,就选择这其中检验量最大的列作为入基就能保证目标函数在最大程度上变小,如果待选列检验量都小于等于0,则代表此时已经得到最小值


入基已经准备就绪,那么如何选择出基呢?即如何选择被替代的基列?在选择入基是其实还有一个隐含的问题,入基被选择后,新的基变量值是多少?这些可以从公式(1)中得到答案:

(7)xB=B1bB1NxN=B1bB1pjxj

xj原来是xN中分量,初始时为0,将这个非基变量相关列选择作为新的基,xj也将获得新的值。

这里设b~=B1b,yj=B1pj。很明显,b~是初始基可行解,可行解xB可写成矩阵形式:

(8)xB=[b1~b2~bm~][yj1yj2yjm]xj=[xB1xB2xBm][yj1yj2yjm]xj=[xB1yj1xjxB2yj2xjxBmyjmxj]

由于约束条件xB0的限制,所以xj的范围也有限制,即xBiyjixj0,也就是xjxBiyji

故此,当xjmin{xBiyji}(yji>0)时,即可保证新生成的xB0

得到xj的值后代入(8),使得原xB中有一个基变量变为0,我们知道xB中每一个分量都是与基的列一一对应的,如果其中一个分量变量变为0,则代表这个基被替换,分量变为0的基为出基。

2.2.3 单纯形法代码#

min4x1x2s.t.x1+2x242x1+3x212x1x23x1,x20

Copy
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()
posted @   码头牛牛  阅读(77)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示
目录