线性规划 Part 2:二阶段单纯形法
在上一篇文章中,我们简要介绍了单纯形法的原理,但仍存在一个问题,就是没法获得初始的可行基。 这可以通过求解另一个线性规划来得到,这就是二阶段单纯形法。
1. 二阶段单纯形法
1.1. Phase I 寻找可行基
在上文中,不知道你有没有发现一个问题:第一步我们计算初始可行解 $\hat{x}_B=A_B^{-1}b$,但如果它并不满足 $\hat{x}_B\ge 0$ 呢?
我们将原始问题记为 $\eqref{eq:p1}$
\[\begin{aligned} \min_{x\in \mathbb{R}^n} &\quad c^{\top} x \\ \text { s.t. } &\quad Ax=b \\ &\quad x \ge 0 \end{aligned} \tag{P} \label{eq:p1}\]那么可以通过添加人工变量(artificial variables),将其转化为另一个辅助问题 $\eqref{eq:p2}$
\[\begin{aligned} \min_{x\in \mathbb{R}^n, w\in \mathbb{R}^m} &\quad e^{\top} w \\ \text { s.t. } &\quad Ax+w=b \\ &\quad x,w \ge 0 \end{aligned} \tag{P2} \label{eq:p2}\]很明显,$(0,b)^{\top}$ 是 $\eqref{eq:p2}$ 的一个基本可行解. 并且如果原问题(P)的可行域 $\mathcal{F}_P\neq \emptyset$,那么(P2)的最优解一定能让 $w$ 取到0. 这时会出现两种情况:
- $w$ 中都是非基变量,此时得到的解 $x$ 就可作为(P)的基本可行解.
- $w$ 中存在基变量,这时需要将基变量出基,再从 $x$ 中寻找变量进基.
1.2. Phase II 代入求解
在得到了可行基之后,我们就能使用之前的修正单纯形法进行求解了。
1.3. 测试
对于优化问题
\[\begin{aligned} \min_{x} &\quad -x_{1}-x_{2} \\ \text { s.t. } &\quad x_{1}+2 x_{3}+x_{4} \leq 1 \\ &\quad x_{2}-x_{3}+x_{5} \leq 1 \\ &\quad x_{1}+x_{2}+x_{3} \leq 2 \\ &\quad x_{\nu} \geq 0 \quad(1 \leq \nu \leq 5) \end{aligned}\]1.3.1. Phase I
我们先求解该问题获得可行基:
\[\begin{aligned} \min_{x} &\quad x_{6} \\ \text { s.t. } &\quad x_{1}+2 x_{3}+x_{4} \leq 1 \\ &\quad x_{2}-x_{3}+x_{5} \leq 1 \\ &\quad x_{1}+x_{2}+x_{3}+x_6 \leq 2 \\ &\quad x_{\nu} \geq 0 \quad(1 \leq \nu \leq 6) \end{aligned}\]很显然,该问题有一个显然的可行基 $B=(4,5,6)$,解得可行基为 $\hat{B}=(1,2,5)$,最优解为 $\hat{x}=(1,1,0,0,1,0)^T$.
1.3.2. Phase 2
可行基中没有人工变量,于是原问题的可行基依然为 $B=(1,2,5)$,更换目标函数后继续代入单纯形法,得到最终解.
import numpy as np
class Data(object):
pass
def LP_simplex(c, A, b, J):
"""
solve linearly constrained optimization problems
min c^T x
s.t. Ax=b
x>=0
B: feasible basis
"""
(m, n) = A.shape
Sn = np.arange(n)
data = Data()
Max_iter_num = 300
for _ in range(Max_iter_num):
K = np.setdiff1d(Sn, J)
A_J = A[:, J]
A_K = A[:, K]
A_J_inv = np.linalg.inv(A_J)
x = np.zeros(n)
x[J] = A_J_inv@b
data.x = x
data.z = np.dot(c, x)
data.J = J
c_J = c[J]
y = A_J_inv.T@c_J
c_bar = c[K]-A_K.T@y
S = np.where(c_bar < 0)
S = S[0]
if len(S) == 0:
data.info = "Success"
return data
sigma = np.argmin(c_bar)
s = K[sigma]
A_s_bar = A_J_inv@A[:, s]
d_J = -A_s_bar
d_K = np.zeros(n-m)
d_K[sigma] = 1
Theta = x[J]/d_J
Theta[Theta >= 0] = -1e+9
rho = np.argmax(Theta)
J[rho] = s
J.sort()
data.info = "Maximum number of iterations exceeded."
return data
if __name__ == "__main__":
A = np.array([[1, 0, 2, 1, 0], [0, 1, -1, 0, 1], [1, 1, 1, 0, 0]])
b = np.array([1, 2, 2])
c = np.array([-1, -1, 0, 0, 0])
# Phase 1
A1 = np.hstack((A, np.array([[0], [0], [1]])))
c1 = np.array([0, 0, 0, 0, 0, 1])
B1 = np.array([4, 5, 6])-1
data = LP_simplex(c1, A1, b, B1)
print(data.info)
print(data.x)
print(data.J)
# Phase 2
data = LP_simplex(c, A, b, data.J)
print(data.info)
print(data.x)
print(data.J)
其结果如下:
Success
[1. 1. 0. 0. 1. 0.]
[0 1 4]
Success
[1. 1. 0. 0. 1.]
[0 1 4]
2. 有界线性规划
以下内容参考于 Optimization—Theory and Practice.
Let the following linear program be given:
\[\begin{aligned} &c^{T} x \longrightarrow \min \\ &A x=b, \quad \ell \leq x \leq u \end{aligned}\]In the following we will use the notations from section $4.1$ (cf. p. $152 \mathrm{ff}$ ).
Show that this problem can be solved via a modification of the revised simplex method:
(1) Determine a feasible basic point $\bar{x}$ to a basis $J$ with
\[\begin{aligned} &\ell_{j} \leq \bar{x}_{j} \leq u_{j} \text { for } j \in J \text { and } \\ &\bar{x}_{j}=\ell_{j} \text { or } \bar{x}_{j}=u_{j} \text { for } j \in K . \end{aligned}\](2) Set \(\bar{c}^{T}:=c_{K}^{T}-y^{T} A_{K}\) with \(y:=A_{J}^{-T} c_{J}\). For all \(s=k_{\sigma} \in K\) let \(\bar{c}_{\sigma} \geq 0\) if \(\bar{x}_{s}=\ell_{s}\) and \(\bar{c}_{\sigma} \leq 0\) if \(\bar{x}_{s}=u_{s}:\) STOP
(3) Otherwise there exists an index \(s=k_{\sigma} \in K\) such that \(\bar{x}_{s}=\ell_{s}\) and \(\bar{c}_{\sigma}<0\) (Case A) or \(\bar{x}_{s}=u_{s}\) and \(\bar{c}_{\sigma}>0\) (Case B).
(4) Define \(d \in \mathbb{R}^{n}\) with \(d_{J}:=-\bar{a}_{s}, d_{K}:=e_{\sigma}\) and \(\bar{a}_{s}:=A_{J}^{-1} a_{s}\).
- Case A: Determine the largest possible \(\tau \geq 0\) such that
Assume that the maximum is attained for $i=\varrho .$ If $\tau \leq u_{s}-$ $\ell_{s}: x_{r}$ with $r=j_{\varrho}$ leaves the basis, $x_{s}$ enters. If $\tau>u_{s}-\ell_{s}$ : Set $\tau:=u_{s}-\ell_{s} ; J$ remains unchanged.
- Case B: Determine the smallest possible $\tau \leq 0$ such that
Assume that the minimum is reached for $i=\varrho .$ If $\tau \geq \ell_{s}-u_{s}$ : $x_{r}$ with $r=j_{\varrho}$ leaves the basis, $x_{s}$ enters. If $\tau<\ell_{s}-u_{s}:$ Set $\tau:=\ell_{s}-u_{s} ; J$ remains unchanged. $\bar{x}:=\bar{x}+\tau d ;$ update of $J$ as described above; goto (2).
3. 实验
Solve the following linear optimization problem
\[\begin{gathered} 2 x_{1}+x_{2}+3 x_{3}-2 x_{4}+10 x_{5} \quad \longrightarrow \min > \\ x_{1}+x_{3}-x_{4}+2 x_{5}=5 \\ x_{2}+2 x_{3}+2 x_{4}+x_{5}=9 \\ 0 \leq x \leq(7,10,1,5,3)^{T} . \end{gathered}\]Firstly, verify that \(J:=(1,2), K:=(3,4,5)\) and \(\bar{x}_{K}:=(0,0,> 0)^{T}\) or \(\bar{x}_{K}:=(1,0,0)^{T}\) yield a feasible basic > solution.
废话就不多说了,直接摆代码:
import numpy as np
class Data(object):
pass
def simplex_bound(c, A, b, L, U, J, x0):
"""
solve linearly constrained optimization problems
min c^T x
s.t. Ax=b
L<=x<=U
B: feasible basis
x0: feasible basic solution
"""
(m, n) = A.shape
Sn = np.arange(n)
data = Data()
Max_iter_num = 300
for _ in range(Max_iter_num):
K = np.setdiff1d(Sn, J)
data.x = x0
data.z = np.dot(c, x0)
data.J = J
A_J = A[:, J]
A_K = A[:, K]
A_J_inv = np.linalg.inv(A_J)
c_J = c[J]
y = A_J_inv.T@c_J
c_bar = c[K]-A_K.T@y
Case = None
for sigma in range(n-m):
s = K[sigma]
if c_bar[sigma] < 0 and x0[s] == L[s]:
Case = 'A'
break
elif c_bar[sigma] > 0 and x0[s] == U[s]:
Case = 'B'
break
# print(f'Case {Case}: ')
A_s_bar = A_J_inv@A[:, s]
d_J = -A_s_bar
d_K = np.zeros(n-m)
d_K[sigma] = 1
if Case is None:
data.info = "Success"
return data
elif Case == 'A':
u = U[J]
l = L[J]
u[A_s_bar > 0] = l[A_s_bar > 0]
Theta = (x0[J]-u)/A_s_bar
rho = np.argmin(Theta)
tau = Theta[rho]
r = J[rho]
if tau < (U[s]-L[s]):
x0[J] += tau*d_J
x0[K] += tau*d_K
J[rho] = s
else:
tau = U[s]-L[s]
x0[J] += tau*d_J
x0[K] += tau*d_K
elif Case == 'B':
u = U[J]
l = L[J]
u[A_s_bar < 0] = l[A_s_bar < 0]
Theta = (x0[J]-u)/A_s_bar
rho = np.argmax(Theta)
tau = Theta[rho]
r = J[rho]
if tau < (L[s]-U[s]):
x0[J] += tau*d_J
x0[K] += tau*d_K
J[rho] = s
else:
tau = L[s]-U[s]
x0[J] += tau*d_J
x0[K] += tau*d_K
J.sort()
data.info = "Maximum number of iterations exceeded."
return data
if __name__ == "__main__":
A = np.array([[1, 0, 1, -1, 2], [0, 1, 2, 2, 1]])
b = np.array([5, 9])
c = np.array([2, 1, 3, -2, 10])
J = np.array([0, 1])
L = np.zeros(5)
U = np.array([7, 10, 1, 5, 3])
x0 = np.array([5.0, 9, 0, 0, 0])
data = simplex_bound(c, A, b, L, U, J, x0)
print(data.x)
print(data.info)
输出结果为:
[7. 1. 1. 3. 0.]
Success
4. 参考资料
- W. Forst and D. Hoffmann, Optimization—Theory and Practice. New York, NY: Springer New York, 2010. https://doi.org/10.1007/978-0-387-78977-4.
- Math 340: Linear Programming - math340notes.pdf