Bootstrap

最优化方法Python计算:求解一般形式线性规划问题的两阶段方法

利用博文《最优化方法Python计算:线性规划的标准化》定义的standardizing函数、《最优化方法Python计算:标准型线性规划的单纯形算法》定义的simplex函数、《最优化方法Python计算:标准型线性规划的辅助问题》定义的prepro函数和《最优化方法Python计算:辅助问题最优解的预后处理》定义的prognostic函数装配成如下的求解一般形式线性规划
{ minimize c ⊤ x s.t.   A i q x ≤ b i q A e q x = b e q x ≥ o ( 1 ) \begin{cases} \text{minimize}\quad\quad\boldsymbol{c}^\top\boldsymbol{x}\\ \text{s.t.\ \ }\quad\quad\boldsymbol{A}_{iq}\boldsymbol{x}\leq\boldsymbol{b}_{iq}\\ \quad\quad\quad\quad\boldsymbol{A}_{eq}\boldsymbol{x}=\boldsymbol{b}_{eq}\\ \quad\quad\quad\quad\boldsymbol{x}\geq\boldsymbol{o} \end{cases}\quad\quad(1) minimizecxs.t.  AiqxbiqAeqx=beqxo(1)
的linprog函数

import numpy as np
def linprog(c, Aiq=None, biq=None, Aeq=None, beq=None):
    if isinstance(Aiq, np.ndarray):
        nx = Aiq.shape[1]
    else:
        nx = Aeq.shape[1]
    A, b = standardizing(Aiq, biq, Aeq, beq)
    E, Bind1 = prepro(A)
    m, n = A.shape
    c = np.concatenate((c, np.zeros(n - c.size)), axis = 0)
    n1 = E.shape[1]
    A1 = np.hstack((A, E))
    Nind1 = np.setdiff1d(np.arange(n + n1), Bind1)
    if n1 > 0:
        c1 = np.concatenate((np.zeros(n), np.ones(n1)), axis = 0)
        Bindst = simplex(A1, b, c1, Bind1, Nind1)
        Bst = A1[:, Bindst]
        Bst1 = np.linalg.inv(Bst)
        xBst = np.matmul(Bst1, b.reshape(m, 1)).flatten()
        art = np.where(Bindst >= n)[0]
        if art.size > 0 and not(np.abs(xBst[art]) < 1e-10).all():
            print('不可行问题...', end=',')
            Bind = None
        else:
            A, Bindst, b=prognostic(A, E, Bindst, b)
            Nindst = np.setdiff1d(np.arange(n), Bindst)
            Bind = simplex(A, b, c, Bindst, Nindst)
    else:
        Bind = simplex(A, b, c, Bind1, Nind1)
    if isinstance(Bind, np.ndarray):
        x = np.zeros(n)
        B = A[:, Bind]
        B1 = np.linalg.inv(B)
        xB = np.matmul(B1, b.reshape(b.size, 1)).flatten()
        x = np.zeros(n)
        x[Bind] = xB
        if n > nx:
            x=(x[:nx],x[nx:])
    else:
        x = None
    return x

程序的第2~41行定义的linprog函数接受的参数参数c表示目标函数的系数向量,Aiq,biq表示不等式约束的系数矩阵和常数向量。Aeq,beq表示等式约束的系数矩阵和常数向量。
函数体中,第3~6行的if-else语句记录原问题决策变量数于nx。第7行调用standardizing函数将输入的线性规划问题标准化,返回标准型等式约束的系数矩阵A和常数向量b。第8行调用prepro函数构造原问题标准型的辅助问题,返回值赋予E和Bind1表示添加的人工变量的系数矩阵和辅助问题的初始基矩阵。第14~29行的if-else语句根据是标准型问题否添加了人工变量,决定一次直接解决原问题(第29行的操作)还是通过两个阶段完成计算(第15~27行的操作)。对有人工变量的情形,第16行调用simplex函数,完成第一阶段求解辅助问题的最优解。返回值赋予表示辅助问题最优解对应的基矩阵 B ∗ \boldsymbol{B}^{*} B向量下标集的Bindst。第17~20行根据Bindst算得问题辅助问题的最优解中对应人工变量非零值的片段art。第21~27行的内嵌if-else语句根据art是否为空判定标准型原问题是否可行。第22~23行处理不可行情形,第25~27行处理可行情形。其中,第25行调用prognostic函数对第一阶段算得的 B ∗ \boldsymbol{B}^{*} B以及标准型的 A \boldsymbol{A} A b \boldsymbol{b} b作预后处理。第27行用重构的标准型问题的系数矩阵A,常数向量b即其初始基矩阵列向量下标Bindst,再次调用simplex,完成第二阶段计算。第30~40行的if-else语句根据当前表示标准型问题最终基矩阵是否为空集,计算最终解x。若基矩阵为空集,意味着原问题不可行或无界,第40行将x置为空。否则第31~38行利用Bind构造标准型问题的最优解x。其中,第37~38行的if语句对具有松弛变量的情形,将最优解分解成原问题变量部分(x[:nx])和松弛变量部分(x[nx:])。利用linprog函数可求解各种形式的线性规划问题。
综合案例: 一个农民承包了6块耕地,共300亩。准备播种小麦、玉米、蔬菜、瓜果这4种农产品。每个地块由于土质地形不一样,单位面积不同作物的收益是不一样的,如下表所示(本案例数据来自https://opt.aliyun.com/platform/case})。

地块1地块2地块3地块4地块5地块6
小麦5005506301000800700
玉米80070060095090930
蔬菜12001040980860880780
瓜果1000960840650600700

6块地的面积分别为42,56,44,39,60和59亩。四种作物分别最多种植76,88,40和96亩。问题是如何安排种植计划,即在没块地中如何安排4种植物种植面积,可以得到最大的总收益。
记设第 i i i中植物在第 j j j块地的种植面积为 s i j s_{ij} sij i = 1 , 2 , 3 , 4 i=1,2,3,4 i=1,2,3,4 j = 1 , 2 , 3 , 4 , 5 , 6 j=1,2,3,4,5,6 j=1,2,3,4,5,6。根据上表,记
P = ( p i j ) 4 × 6 = ( 500 550 630 1000 800 700 800 700 600 950 90 930 1200 1040 980 860 880 780 1000 960 840 650 600 700 ) , \boldsymbol{P}=(p_{ij})_{4\times 6}=\begin{pmatrix}500&550&630&1000&800&700\\800&700&600&950&90&930\\1200&1040&980&860&880&780\\1000&960&840&650&600&700\end{pmatrix}, P=(pij)4×6= 500800120010005507001040960630600980840100095086065080090880600700930780700 ,
则总收益为
f = ∑ i = 1 4 ∑ j = 1 6 p i j s i j . f=\sum_{i=1}^4\sum_{j=1}^6p_{ij}s_{ij}. f=i=14j=16pijsij.
受地块大小的限制,有
∑ i = 1 4 s i 1 ≤ 42 , ∑ i = 1 4 s i 2 ≤ 46 , ∑ i = 1 4 s i 3 ≤ 44 , ∑ i = 1 4 s i 4 ≤ 39 , ∑ i = 1 4 s i 5 ≤ 60 , ∑ i = 1 4 s i 6 ≤ 59. \begin{array}{l} \sum\limits_{i=1}^4s_{i1}\leq42,\\ \sum\limits_{i=1}^4s_{i2}\leq46,\\ \sum\limits_{i=1}^4s_{i3}\leq44,\\ \sum\limits_{i=1}^4s_{i4}\leq39,\\ \sum\limits_{i=1}^4s_{i5}\leq60,\\ \sum\limits_{i=1}^4s_{i6}\leq59. \end{array} i=14si142,i=14si246,i=14si344,i=14si439,i=14si560,i=14si659.
又由于每种作物的最大种植面积的限制,有
∑ j = 1 6 s 1 j ≤ 76 , ∑ j = 1 6 s 2 j ≤ 88 , ∑ j = 1 6 s 3 j ≤ 40 , ∑ j = 1 6 s 4 j ≤ 96. \begin{array}{l} \sum\limits_{j=1}^6s_{1j}\leq76,\\ \sum\limits_{j=1}^6s_{2j}\leq88,\\ \sum\limits_{j=1}^6s_{3j}\leq40,\\ \sum\limits_{j=1}^6s_{4j}\leq96. \end{array} j=16s1j76,j=16s2j88,j=16s3j40,j=16s4j96.
为便于将问题表示成线性规划模型,令 k = 6 ( i − 1 ) + j k=6(i-1)+j k=6(i1)+j i = 1 , 2 , 3 , 4 i=1,2,3,4 i=1,2,3,4 j = 1 , 2 , 3 , 4 , 5 , 6 j=1,2,3,4,5,6 j=1,2,3,4,5,6),则 s i j = x k s_{ij}=x_k sij=xk p i j = c k p_{ij}=c_k pij=ck k = 1 , 2 , ⋯   , 24 k=1,2,\cdots,24 k=1,2,,24。于是,本问题模型化为线性规划:
{ maximize ∑ k = 1 24 c k x k s.t.  ∑ i = 1 4 x 6 ( i − 1 ) + 1 ≤ 42 , ∑ i = 1 4 x 6 ( i − 1 ) + 2 ≤ 46 , ∑ i = 1 4 x 6 ( i − 1 ) + 3 ≤ 44 , ∑ i = 1 4 x 6 ( i − 1 ) + 4 ≤ 39 , ∑ i = 1 4 x 6 ( i − 1 ) + 5 ≤ 60 , ∑ i = 1 4 x 6 ( i − 1 ) + 6 ≤ 59 , ∑ j = 1 6 x j ≤ 76 , ∑ j = 1 6 x 6 + j ≤ 88 , ∑ j = 1 6 x 12 + j ≤ 40 , ∑ j = 1 6 x 18 + j ≤ 96. \begin{cases} \text{maximize}\quad\sum\limits_{k=1}^{24}c_kx_k\\ \text{s.t.\ }\quad\quad\sum\limits_{i=1}^4x_{6(i-1)+1}\leq42,\\ \quad\quad\quad\quad\sum\limits_{i=1}^4x_{6(i-1)+2}\leq46,\\ \quad\quad\quad\quad\sum\limits_{i=1}^4x_{6(i-1)+3}\leq44,\\ \quad\quad\quad\quad\sum\limits_{i=1}^4x_{6(i-1)+4}\leq39,\\ \quad\quad\quad\quad\sum\limits_{i=1}^4x_{6(i-1)+5}\leq60,\\ \quad\quad\quad\quad\sum\limits_{i=1}^4x_{6(i-1)+6}\leq59,\\ \quad\quad\quad\quad\sum\limits_{j=1}^6x_{j}\leq76,\\ \quad\quad\quad\quad\sum\limits_{j=1}^6x_{6+j}\leq88,\\ \quad\quad\quad\quad\sum\limits_{j=1}^6x_{12+j}\leq40,\\ \quad\quad\quad\quad\sum\limits_{j=1}^6x_{18+j}\leq96. \end{cases} maximizek=124ckxks.t. i=14x6(i1)+142,i=14x6(i1)+246,i=14x6(i1)+344,i=14x6(i1)+439,i=14x6(i1)+560,i=14x6(i1)+659,j=16xj76,j=16x6+j88,j=16x12+j40,j=16x18+j96.
c = ( c 1 ⋮ c 24 ) \boldsymbol{c}=\begin{pmatrix}c_1\\\vdots\\c_{24}\end{pmatrix} c= c1c24 x = ( x 1 ⋮ x 24 ) \boldsymbol{x}=\begin{pmatrix}x_1\\\vdots\\x_{24}\end{pmatrix} x= x1x24
A i q = ( 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 ) , \boldsymbol{A}_{iq}=\begin{pmatrix}1&0&0&0&0&0&1&0&0&0&0&0&1&0&0&0&0&0&1&0&0&0&0&0\\0&1&0&0&0&0&0&1&0&0&0&0&0&1&0&0&0&0&0&1&0&0&0&0\\0&0&1&0&0&0&0&0&1&0&0&0&0&0&1&0&0&0&0&0&1&0&0&0\\0&0&0&1&0&0&0&0&0&1&0&0&0&0&0&1&0&0&0&0&0&1&0&0\\0&0&0&0&1&0&0&0&0&0&1&0&0&0&0&0&1&0&0&0&0&0&1&0\\0&0&0&0&0&1&0&0&0&0&0&1&0&0&0&0&0&1&0&0&0&0&0&1\\1&1&1&1&1&1&0&0&0&0&0&0&0&0&0&0&0&0&0&0&0&0&0&0\\0&0&0&0&0&0&1&1&1&1&1&1&0&0&0&0&0&0&0&0&0&0&0&0\\0&0&0&0&0&0&0&0&0&0&0&0&1&1&1&1&1&1&0&0&0&0&0&0\\0&0&0&0&0&0&0&0&0&0&0&0&0&0&0&0&0&0&1&1&1&1&1&1 \end{pmatrix}, Aiq= 100000100001000010000010001000000100100000001010000000011000100000010001000001000010000100000100010000001001000000010100100000001001000000100010000010000100001000001000100000010010100000000101000000010010000001000100000100001000010000010001 ,
b i q ⊤ = ( 42 , 46 , 44 , 39 , 60 , 59 , 76 , 88 , 40 , 96 ) . \boldsymbol{b}_{iq}^\top=(42,46,44,39,60,59,76,88,40,96). biq=(42,46,44,39,60,59,76,88,40,96).
则线性规划的矩阵形式为
{ minmize − c ⊤ x s.t. A i q x ≤ b i q x ≥ o . \begin{cases} \text{minmize}\quad\quad-\boldsymbol{c}^\top\boldsymbol{x}\\ \text{s.t.}\quad\quad\quad\quad\boldsymbol{A}_{iq}\boldsymbol{x}\leq\boldsymbol{b}_{iq}\\ \quad\quad\quad\quad\quad\quad\boldsymbol{x}\geq\boldsymbol{o} \end{cases}. minmizecxs.t.Aiqxbiqxo.
下列代码完成本案例的求解计算。

import numpy as np										#导入numpy
from fractions import Fraction as F						#设置输出格式
np.set_printoptions(formatter = {'all':lambda x:
                               str(F(x).limit_denominator())})
P = np.array([[500, 550, 630, 1000, 800, 700],			#单位面积收益矩阵
              [800, 700, 600, 950, 90, 930],
              [1200, 1040, 980, 860, 880, 280],
              [1000, 960, 840, 650, 600, 700]])
c = -P.flatten()										#目标函数系数向量
Up = np.eye(6)
Lo = np.zeros((4, 6))
Lo[0] = np.ones(6)
for i in range(3):										#构造系数矩阵组成块
    Up = np.hstack((Up, np.eye(6)))
    T = np.zeros((4, 6))
    T[i + 1] = np.ones(6)
    Lo = np.hstack((Lo,T))
Ai = np.vstack((Up, Lo))								#不等式约束系数矩阵
bi = np.array([42, 46, 44, 39, 60, 59, 76, 88, 40, 96])	#常数向量
linprog(c, Ai, bi)

程序的第5~8行将表格所列数据构造矩阵P。第9行按行优先顺序将P扁平化为并取负值赋予表示目标函数系数向量的c。注意到问题的不等式约束矩阵 A i q \boldsymbol{A}_{iq} Aiq的结构:前6行由4个6阶单位阵并列而成。后4行由4个 4 × 6 4\times6 4×6的矩阵并列而成,每一个均有一行全为1,其余元素为0。元素为1的行位于第 i i i行, i = 1 , 2 , 3 , 4 i=1,2,3,4 i=1,2,3,4。按此观察,程序的第10行将Up初始化为6阶单位阵,第11~12行将Lo初始化为第1行元素为1,其余元素全为0的 4 × 6 4\times6 4×6矩阵。第13~17行的for循环,将Up和Lo分别组成系数矩阵 A i q \boldsymbol{A}_{iq} Aiq的前6行和后4行。第18行将Up和Lo纵向叠加为系数矩阵,赋予Ai。第19行设置不等式约束的常数向量 b i q \boldsymbol{b}_{iq} biq为bi。运行程序,输出

(array([0, 0, 0, 16, 60, 0, 0, 0, 0, 23, 0, 59,
        40, 0, 0, 0, 0, 0, 2, 46, 44, 0, 0, 0]),
 array([0, 0, 0, 0, 0, 0, 0, 6, 0, 4]))

意为问题的最优解为 x 0 ⊤ = ( 0 , 0 , 0 , 16 , 60 , 0 , 0 , 0 , 0 , 23 , 0 , 59 , 40 , 0 , 0 , 0 , 0 , 0 , 2 , 46 , 44 , 0 , 0 , 0 ) \boldsymbol{x}_0^\top=(0, 0, 0, 16, 60, 0, 0, 0, 0, 23, 0, 59, 40, 0, 0, 0, 0, 0, 2, 46, 44, 0, 0, 0) x0=(0,0,0,16,60,0,0,0,0,23,0,59,40,0,0,0,0,0,2,46,44,0,0,0),松弛变量对应值为 ( x 25 , x 26 , x 27 , x 28 , x 29 , x 30 , x 31 , x 32 , x 33 , x 34 ) = ( 0 , 0 , 0 , 0 , 0 , 0 , 0 , 6 , 0 , 4 ) (x_{25},x_{26},x_{27},x_{28},x_{29},x_{30},x_{31},x_{32},x_{33},x_{34})=(0, 0, 0, 0, 0, 0, 0, 6, 0, 4) (x25,x26,x27,x28,x29,x30,x31,x32,x33,x34)=(0,0,0,0,0,0,0,6,0,4)。即小麦在4号地块种植16亩,第5号地块种植60亩;玉米在4号地块种23亩,6号地块种59亩;蔬菜在1号地块种40亩;瓜果在1号地块种2亩,2号地块种46亩,3号地块种44亩可使收益最大。松弛变量 x 32 = 6 x_{32}=6 x32=6意味着玉米实际种植面积距最大计划种植面积少6亩。相仿地,瓜果实际种植面积比最大计划种植面积少 x 34 = 4 x_{34}=4 x34=4亩。
写博不易,敬请支持:
如果阅读本文于您有所获,敬请点赞、评论、收藏,谢谢大家的支持!

;