算法设计与分析课程复习笔记7——动态规划
动态规划
- 和分治法一样,是一种算法设计技术。
- 子问题非独立。
- 分治法通过递归方式解决性质相同的子问题,
而动态规划每次解决一个子问题,并将结果存储在表格中。 - 用于优化类问题。
算法:
- 描述最优解的结构特征
- 定义最优解决方案的递归形式
- 以自底向上的方式计算最优解决方案的值
- 从计算信息构造出最优解决方案
装配线排程
S
1
,
1
,
S
1
,
2
,
…
…
,
S
1
,
n
;
S
2
,
1
,
S
2
,
2
,
…
…
,
S
2
,
n
S_{1,1},S_{1,2},……,S_{1,n};S_{2,1},S_{2,2},……,S_{2,n}
S1,1,S1,2,……,S1,n;S2,1,S2,2,……,S2,n为两条装配线的工序站台
a
1
,
1
,
a
1
,
2
,
…
…
,
a
1
,
n
;
a
2
,
1
,
a
2
,
2
,
…
…
,
a
2
,
n
a_{1,1},a_{1,2},……,a_{1,n};a_{2,1},a_{2,2},……,a_{2,n}
a1,1,a1,2,……,a1,n;a2,1,a2,2,……,a2,n为两条装配线的各站台工作时间
每条装配线的第j个站台的功能相同,但是效率不一致,即花费时间不同。
另外有上线时间
e
1
,
e
2
e_1,e_2
e1,e2和下线时间
x
1
,
x
2
x_1,x_2
x1,x2
以及从一条装配线变换到另一条装配线需要的时间
t
1
,
1
,
t
1
,
2
,
…
…
,
t
1
,
n
−
1
;
t
2
,
1
,
t
2
,
2
,
…
…
,
t
2
,
n
−
1
t_{1,1},t_{1,2},……,t_{1,n-1};t_{2,1},t_{2,2},……,t_{2,n-1}
t1,1,t1,2,……,t1,n−1;t2,1,t2,2,……,t2,n−1
问题:如何充分利用两条装配线,使得组装一辆汽车的时间最短?
解决方法:
1️⃣蛮力法
计算装配线排程所有可能的组合情况,比较并选择出最短时间的组合
2️⃣动态规划
【1】.构建最优解
考虑所有从起点到达
S
1
,
j
S_{1,j}
S1,j可能途径
只有两种可能:
①从
S
1
,
j
−
1
S_{1,j-1}
S1,j−1直接到
S
1
,
j
S_{1,j}
S1,j
②从
S
2
,
j
−
1
S_{2,j-1}
S2,j−1花费
t
2
,
j
−
1
t_{2,j-1}
t2,j−1时间转换到
S
1
,
j
S_{1,j}
S1,j
如果到达
S
1
,
j
S_{1,j}
S1,j的最快装配路线来自
S
1
,
j
−
1
S_{1,j-1}
S1,j−1那么必须是从装配线起点经过
S
1
,
j
−
1
S_{1,j-1}
S1,j−1的最快装配路线。
S
2
,
j
−
1
S_{2,j-1}
S2,j−1同理分析。
最优解的结构
寻求从起点到达
S
1
,
j
S_{1,j}
S1,j最快装配路线,可分解为寻求从起点经过
S
1
,
j
−
1
S_{1,j-1}
S1,j−1 or
S
2
,
j
−
1
S_{2,j-1}
S2,j−1 最快装配路线问题。
我们将这种具有分解递归特征的解的形式称为最优化结构特征。
利用这种优化构造特征,从子问题的最优化解获得整个问题的最优化的解。
【2】.递归解
利用子问题的最优解,通过递归的方式求解原问题的最优解
f
∗
f*
f∗为完成所有装配过程的最短时间。
f
i
[
j
]
f_i[j]
fi[j]表示从起点经过Si,j工序的最短时间.
f
∗
=
m
i
n
(
f
1
[
n
]
+
x
1
,
f
2
[
n
]
+
x
2
)
f* = min (f_1[n] + x_1, f_2[n] + x_2)
f∗=min(f1[n]+x1,f2[n]+x2)
当
j
=
1
j=1
j=1时
f
1
[
1
]
=
e
1
+
a
1
,
1
f_1[1] = e_1 + a_{1,1}
f1[1]=e1+a1,1
f
2
[
1
]
=
e
2
+
a
2
,
1
f_2[1] = e_2 + a_{2,1}
f2[1]=e2+a2,1
当
j
≥
2
j≥2
j≥2时
f
1
[
j
]
=
m
i
n
(
f
1
[
j
−
1
]
+
a
1
,
j
,
f
2
[
j
−
1
]
+
t
2
,
j
−
1
+
a
1
,
j
)
f_1[j] = min(f_1[j - 1] + a_{1,j} ,f_2[j -1] + t_{2,j-1} + a_{1,j})
f1[j]=min(f1[j−1]+a1,j,f2[j−1]+t2,j−1+a1,j)
f
2
[
j
]
=
m
i
n
(
f
2
[
j
−
1
]
+
a
2
,
j
,
f
1
[
j
−
1
]
+
t
1
,
j
−
1
+
a
2
,
j
)
f_2[j] = min(f_2[j - 1] + a_{2,j} ,f_1[j -1] + t_{1,j-1} + a_{2,j})
f2[j]=min(f2[j−1]+a2,j,f1[j−1]+t1,j−1+a2,j)
【3】.计算最优解
如果自顶向下求最优解:
那么将导致指数增长的计算时间。
所以我们选择按
j
j
j递增,即自底向上的方式求解。
【4】.构建最优方案
算法:
FastestWay(a,t,e,x,n)
f
1
[
1
]
←
e
1
+
a
1
,
1
f_1[1] \leftarrow e_1+a_{1,1}
f1[1]←e1+a1,1
f
2
[
1
]
←
e
2
+
a
2
,
1
f_2[1] \leftarrow e_2+a_{2,1}
f2[1]←e2+a2,1
for j=2 to n
do if
f
1
[
j
−
1
]
+
a
1
,
j
f_1[j-1]+a_{1,j}
f1[j−1]+a1,j ≤
f
2
[
j
−
1
]
+
t
2
,
j
−
1
+
a
1
,
j
f_2[j-1]+t_{2,j-1}+a_{1,j}
f2[j−1]+t2,j−1+a1,j
then
f
1
[
j
]
←
f
1
[
j
−
1
]
+
a
1
,
j
f_1[j] \leftarrow f_1[j-1]+a_{1,j}
f1[j]←f1[j−1]+a1,j
I
1
[
j
]
←
1
I_1[j] \leftarrow 1
I1[j]←1
else
f
1
[
j
]
←
f
2
[
j
−
1
]
+
t
2
,
j
−
1
+
a
1
,
j
f_1[j] \leftarrow f_2[j-1]+t_{2,j-1}+a_{1,j}
f1[j]←f2[j−1]+t2,j−1+a1,j
I
1
[
j
]
←
2
I_1[j] \leftarrow 2
I1[j]←2
if
f
2
[
j
−
1
]
+
a
2
,
j
f_2[j-1]+a_{2,j}
f2[j−1]+a2,j ≤
f
1
[
j
−
1
]
+
t
1
,
j
−
1
+
a
2
,
j
f_1[j-1]+t_{1,j-1}+a_{2,j}
f1[j−1]+t1,j−1+a2,j
then
f
2
[
j
]
←
f
2
[
j
−
1
]
+
a
2
,
j
f_2[j] \leftarrow f_2[j-1]+a_{2,j}
f2[j]←f2[j−1]+a2,j
I
1
[
j
]
←
2
I_1[j] \leftarrow 2
I1[j]←2
else
f
2
[
j
]
←
f
1
[
j
−
1
]
+
t
1
,
j
−
1
+
a
2
,
j
f_2[j] \leftarrow f_1[j-1]+t_{1,j-1}+a_{2,j}
f2[j]←f1[j−1]+t1,j−1+a2,j
I
1
[
j
]
←
1
I_1[j] \leftarrow 1
I1[j]←1
if
f
1
[
n
]
+
x
1
≤
f
2
[
n
]
+
x
2
f_1[n]+x_1 ≤ f_2[n] + x_2
f1[n]+x1≤f2[n]+x2
then
f
∗
=
f
1
[
n
]
+
x
1
f* = f_1[n]+x_1
f∗=f1[n]+x1
I
∗
=
1
I* = 1
I∗=1
else
f
∗
=
f
2
[
n
]
+
x
2
f* = f_2[n]+x_2
f∗=f2[n]+x2
I
∗
=
2
I* = 2
I∗=2
PrintStation(I,n)
i ← I*
print “line” i “,station” n
for j ← n downto 2
do i ←
I
i
[
j
]
I_i[j]
Ii[j]
print “line” i “,station” j-1
矩阵链相乘
给定矩阵序列
A
1
,
A
2
,
…
,
A
n
A_1, A_2, …, A_n
A1,A2,…,An,求它们的积
C
=
A
∗
B
C=A*B
C=A∗B
c
o
l
A
=
r
o
w
B
col_A=row_B
colA=rowB
r
o
w
C
=
r
o
w
A
row_C=row_A
rowC=rowA
c
o
l
C
=
c
o
l
B
col_C=col_B
colC=colB
A
1
,
A
2
,
…
,
A
n
A_1, A_2, …, A_n
A1,A2,…,An
c
o
l
i
=
r
o
w
i
+
1
col_i = row_{i+1}
coli=rowi+1
矩阵链相乘的顺序极大的影响计算的代价
MatrixMultiply(A,B)
if columns[A] ≠ rows[B]
then error “incompatible dimensions”
else for i ← 1 to rows[A]
do for j ← 1 to columns[B]
do C[i,j]=0
for k ← 1 to columns[A]
do C[i,j] ← C[i,j] + A[i,k]B[k.j]
A
1
,
A
2
,
…
…
,
A
n
A_1, A_2, ……, A_n
A1, A2, ……, An
p
0
∗
p
1
,
p
1
∗
p
2
,
…
…
,
p
n
−
1
∗
p
n
p_0*p_1,p_1*p_2,……,p_{n-1}*p_n
p0∗p1,p1∗p2,……,pn−1∗pn
如何决定矩阵链相乘的顺序,即如何放置括号,使矩阵链相乘所需要的数量乘法的次数最小
1️⃣蛮力法
逐一比较
2️⃣动态规划
【1】.最优矩阵链相乘顺序结构
标记
A
i
…
j
A_{i…j}
Ai…j=
A
i
,
A
i
+
1
,
…
…
,
A
j
A_i,A_{i+1},……,A_j
Ai,Ai+1,……,Aj=
A
i
…
k
A
k
+
1
…
j
A_{i…k}A_{k+1…j}
Ai…kAk+1…j
假设矩阵链 A i … j A_{i…j} Ai…j相乘的最优顺序在 A k A_k Ak和 A k + 1 A_{k+1} Ak+1分割
【2】.递归解
求
A
i
…
j
A_{i…j}
Ai…j数量乘法的次数最小的运算顺序
利用m[i, j]标记
A
i
…
j
A_{i…j}
Ai…j最小的数量乘法次数
则
i
f
i
=
j
if i=j
if i=j
m
[
i
,
j
]
=
0
m[i,j]=0
m[i,j]=0
i
f
i
<
j
if i<j
if i<j
m
[
i
,
j
]
=
m
i
n
i
≤
k
≤
j
{
m
[
i
,
k
]
+
m
[
k
+
1
,
j
]
+
p
i
−
1
p
k
p
j
}
m[i,j]=min_{i≤k≤j}\{m[i,k]+m[k+1,j]+p_{i-1}p_kp_j\}
m[i,j]=mini≤k≤j{m[i,k]+m[k+1,j]+pi−1pkpj}
动态规划的适用性
- 最优解包含子问题的最优解
- 递归算法一次又一次地重复同样的问题;只有Θ(n2)个子问题。
【3】.计算解
Matrix-Chain-Order(p)
n ← length[p]-1;
for i ← 1 to n
m[i, i] ← 0;
for I ← 2 to n
for i ← 1 to n – l +1
j ← i + l -1;
m[i, j] ←
∞
\infty
∞;
for k ← i to j -1
q ← m[i, k] + m[k+1, j] +
p
I
−
1
p
k
p
j
p_{I-1}p_kp_j
pI−1pkpj;
if q < m[i, j]
m[i,j] ← q;
s[i, j] ← k;
return m and s
【4】.构造最优相乘顺序
s[i, j]:记录了
A
i
A
i
+
1
…
A
j
A_i A_{i+1} … A_j
AiAi+1…Aj的最优分割顺序位置
Matrix-Chain-Multiply(A, s, i, j)
if j > i
X ← Matrix-Chain-Multiply(A, s, i, s[i,j]);
Y ← Matrix-Chain-Multiply(A, s, s[i, j]+1, j);
return Matrix-Multiply(X, Y);
else return
A
i
A_i
Ai;
最长共同子序列LCS
Z =(B,C,A)是X,Y的一个共同子序列
X = (A,B,C,B,D,A,B)
Y = (B, D, C, A, B, A)
X,Y的最长共同子序列为
Z
′
Z'
Z′=(B, D, A, B)
解决方法
1️⃣蛮力法
穷举X的所有子序列,检查其是否在Y中出现,然后选出LCS
2️⃣动态规划
【1】.最优解结构
X
=
(
x
1
,
x
2
,
…
…
,
x
m
)
X=(x_1,x_2,……,x_m)
X=(x1,x2,……,xm)
Y
=
(
y
1
,
y
2
,
…
…
,
y
n
)
Y=(y_1,y_2,……,y_n)
Y=(y1,y2,……,yn)
L
C
S
=
Z
=
(
z
1
,
z
2
,
…
…
z
k
)
LCS=Z=(z_1,z_2,……z_k)
LCS=Z=(z1,z2,……zk)
如果
x
m
=
y
n
x_m=y_n
xm=yn,那么
z
k
=
x
m
=
y
n
z_k=x_m=y_n
zk=xm=yn,
Z
k
−
1
Z_{k-1}
Zk−1是
X
m
−
1
X_{m-1}
Xm−1和
Y
n
−
1
Y_{n-1}
Yn−1的LCS
如果
x
m
x_m
xm≠
y
n
y_n
yn,那么
z
k
z_k
zk≠
x
m
x_m
xm就意味着
Z
Z
Z是
X
m
−
1
X_{m-1}
Xm−1和
Y
Y
Y的LCS
如果
x
m
x_m
xm≠
y
n
y_n
yn,那么
z
k
z_k
zk≠
y
n
y_n
yn就意味着
Z
Z
Z是
X
X
X和
Y
n
−
1
Y_{n-1}
Yn−1的LCS
【2】.递归解
C:X 和Y最长共同子序列的长度
c[i,j]:
X
i
X_i
Xi 和
Y
j
Y_j
Yj最长共同子序列的长度
问题的解即为c[m,n]
c [ i , j ] = { 0 , i f i = 0 o r j = 0 c [ i − 1 , j − 1 ] + 1 , i f x i = y j , i , j > 0 m a x ( c [ i , j − 1 ] , c [ i − 1 , j ] ) , i f x i ≠ y j , i , j > 0 c[i,j]=\left\{ \begin{aligned} 0, if i=0 or j=0\\ c[i-1,j-1]+1, if x_i=y_j,i,j>0\\ max(c[i,j-1],c[i-1,j]), if x_i \neq y_j,i,j>0 \end{aligned} \right. c[i,j]=⎩⎪⎨⎪⎧0, if i=0 or j=0c[i−1,j−1]+1, if xi=yj,i,j>0max(c[i,j−1],c[i−1,j]), if xi̸=yj,i,j>0
【3】.计算解
LCS算法:
LCSlength(X,Y)
m ← length(X)
n ← length(Y)
for i ← 1 to m
c[i,0] ← 0
for j ← 0 to n
c[0,j] ← 0
for i ← 1 to m
for j ← 1 to n
if
x
i
x_i
xi=
y
j
y_j
yj
c[i,j] ← c[i-1,j-1] + 1
b[i,j] ← “↖”
else if c[i-1,j] ≥ c[i,j-1]
c[i,j] ← c[i-1,j]
b[i,j] ← “↑”
else c[i,j] ← c[i,j-1]
b[i,j] ← “←”
return c and b
算法分析:其实就是简单的填表,一个格子的计算量为O(1),总共m*n个格子,所以总计算开销 O ( m n ) O(mn) O(mn)
【4】.构建最长共同子序列
参考:任课教师邱德红教授的课件