Algorithm - Dynamic programming

Dynamic programming

將已計算的結果記錄在表格中的技巧,目的是為了要避免重複計算相同子問題,以「Button up」的方式實踐。

  • 為何要使用這種方法,以費氏數列程式開始討論。

Fn={0,ifn=01,ifn=1Fn1+Fn2,ifn2

欲求出 F5 ,以遞迴程式執行,會造成「Overlapping subproblem」,如下圖 F2 重複計算了 3 次,F3 重複計算 2 次。

overlappingsubproblem

使用動態表格執行之,可以省去很多不必要的重複計算,如下圖。

fibonassidp
  • 構思動態規劃題目的流程
    • Optimal substructure:一個母問題的最佳解如何由其子問題的最佳解構成。
observedp

Shortest path problem

  • Optimal substructure
shortestpathproblem_subproblem

(98 交大資工) 問 longest path problem 有無 optimal substructure。

無,所以不可以使用動態規劃解此問題。

longestpathproblem

但是如果此圖加上「Acyclic」、「Direct」等條件限制之後,可以用動態規劃解「longest path problem」。

通常要在各種條件之下都可以使用動態規劃解此問題,才可以宣稱此問題可以使用動態規劃解

  • Ex (99 交大)
    • 下列何者正確
      • (a)Dynamic programming always provides polynominal time algorithm.
      • (b)Huffman coding for compresion is a typical dynamic programming algorithm.
      • (c)Dynamic programming use the table to design the algorithm.
      • (d)Optimal is a important element in the dynamic programming.
      • (e)Single source all shortest path problem has optimal substructure.
    • Ans
      • (a):false。反例為 Subset-sum problem (暴力法:O(2n)O(n2n2))
      • (b):false。為典型的 Greedy algorithm。
      • (c)
      • (d)
      • (e)

Knapsack problem

Fractional knapsack problem

  1. 此方法僅限於物品重量為正整數時
  2. 0/1 KP 為 NP - Completed 問題
  • Input:
    • n 個物件
      • 第 i 個重量為 wi ,價值為 vi
    • 背包最大負重
      • W
  • Output:
    • 最大的獲利值
  • 限制條件:
    • 取得物品的總重量 W
    • 可取物品的部分
  • 想法
    • Greedy:從目前 viwi 最高的物品開始拿取,直到物品取完,或是取得物品負重已達 W。
  • Time Complexity
    • Θ(nlogn)
  • Ex ( W = 5 )
Item vi wi
1 10 2
2 6 1
3 12 3

v2w2=6v1w1=5v3w3=4

  1. 取物品 2,取該物品之 1 單位重量,背包剩餘空間 4 單位重量,獲利 6 單位價值。
  2. 取物品 1,取物品之 2 單位重量,背包剩餘空間 2 單位重量,獲利 16 單位價值。
  3. 取物品 3,取物品之 2 單位重量,背包剩餘空間 0 單位重量,獲利 24 單位價值
kpdp
0 1 2 3 4 5
0 0 0 0 0 0 0
1 0 0 10 10 10 10
2 0 6 10 16 16 16
3 0 6 10 16 18 22
  • Ex ( 98 交大 ) P.3-66 ex10
    • Maximize ni=1vixisubjecttoni=1wixiW,0xi1
    • Greedy choise property ( v1w1v2w2 )
    • 最佳解的 x1 需取多少。

xn={1,ifw1WWw1,ifw1>W

0-1 Knapsack problem

  • Input:

    • n 個物件
      • 第 i 個重量為 wi ,價值為 vi
    • 背包最大負重
      • W
  • Output:

    • 最大的獲利值
  • 限制條件:

    • 取得物品的總重量 W
    • 只能取物品的整體
  • 想法

    • 無法使用 Greedy method 解決

    • 使用動態規劃解決,物品的重量必為正整數

      • 遞迴結構:(令 C[i][k] 在負重 k 之下考慮物品 1 ... i 之最大獲利

      C[i][k]={0,ifi=0ORk=0max(C[i1][kwi]+vi,C[i1][k]),ifwikC[i1][k],ifwi>k

  • Algorithm ( Bottom up )

1
2
3
4
5
6
7
8
9
10
11
12
13
for k <- 0 to w
c[0, k] <- 0
for i <- 1 to n
{
c[i, 0] <- 0
for k <- 1 to w
{
if k < w_i
c[i, k] <- c[i-1, k]
else
c[i, k] <- max(c[i-1, k], c[i-1, k-w_i]+v_i)
}
}
  • Time complexity
    • Θ(nW) Pseudo-polynominal
  • Space complexity
    • Θ(nW)

Branch and bound 解 0-1 Knapsack problem

  • 對於一個 NP-Completed 問題來說,可以使用「Branch and bound」來解決
  • 「Branch and bound」演算法中,「Bounding function」的設計會是影響整體效能最大的關鍵 ( 收斂快慢 )。
  • Time complexity:O( 葉節點個數 )
    • 葉節點個數取決問題本身。
      • 組合性問題:2n
      • 排列性問題:n!
  • 將球最佳解的過程視為在一個「State space tree」中尋找最好的節點。

  • 實務上,通常是設計一 個「Bounding function」以估計目前狀態可到最佳解的可能性。

    • 建構「State space tree」時,每次都先展開「Bounding function」的節點。( Branch )
    • 在每次展開的過程中,都可以得到一個目前最佳解 (葉節點),接著,之後不展開「Bounding function 值 目前最佳解」的內部節點。( Bound )
  • 以 Branch and bound 解 0-1 Knapsack problem

    • 每個節點須紀錄
      • 目前的獲利
      • 目前的負重
      • 「Bounding function」算出的值
  1. 為了計算「Bounding funciton」括號中要估計「Fractional knapsack」的未來最大值,需要將物品依照 viwi 排序。

w=4

Item vi wi
1 6 1
2 10 2
3 12 3

v2w2=6v1w1=5v3w3=4

  1. 設計「Bounding function」。(括號部分就是用來估計以目前節點拓展,獲利的上限)

Boundingfunciotn()=+(Fractionalknapsackproblem)

KPstatesearchtree
  1. 拓展節點
    1. 展開 Root
    2. 因為該節點「Bounding function」最大,所以展開 A
    3. 展開節點 C
    4. E 節點因為超重所以為「Infeasiable solution」
    5. F 為一可能解,使 Max=16
    6. 因為節點 D 在樹中較深處,先展開之
    7. GH 均為一解,設 Max=18
    8. 因為節點 B 的「Bounding function」 Max,不展開該節點
  2. Ans:18 ( 取物品一物品三 )

Longest Common Subsequence

  • Sequence
    • X = <a, b, c, a>
  • Subsequence
    • <a, c> 為 X 的「Subsequence」
  • Prefix ( 前綴 )
    • X3=a,b,c
  • Common subsequence
    • Y = <a, c, b, c>
    • 則<a, c>為 X 與 Y 的「Common subsequence」
  • Longest common subsequence
    • <a, b, c> 為 X 與 Y 的「LCS」

「LCS」不一定唯一

  • 遞迴結構
    • c[i,j]LCS(Xi,Yj) 的長,則:

c[i,j]={0,ifi=0ORj=0c[i1,j1]+1,ifX[i]=Y[j]max(c[i1,j],c[i,j1]),ifX[i]Y[j]LCS

  • 演算法 ( Bottom-up )
1
2
3
4
5
6
7
8
9
10
11
12
for j <- 0 to n
c[0, j] <- 0
for i <- 0 to m
c[i, 0] <- 0
for i <- 1 to m
for j <- 1 to n
{
if(X[i] = Y[i])
c[i, j] = c[i-1, j-1] + 1
else
c[i, j] = max(c[i-1, j], c[i, j-1])
}
  • Time complexity
    • Θ(mn)
  • Space complexity
    • Θ(mn)
  • Ex
    • X = <a, b, a, c>
    • Y = <a, b, c, a>
    • 求「LCS」
- - - "a" "b" "a" "c"
- - 0 1 2 3 4
- 0 0 0 0 0 0
"a" 1 0 1(↖) 1(←) 1(↖) 1(←)
"b" 2 0 1(↑) 2(↖) 2(←) 2(←)
"c" 3 0 1(↑) 2(↑) 2(←) 3(↖)
"a" 4 0 1(↖) 2(↑) 3(↖) 3(←)

Longest Increasing Subsequence

  • Ex
    • X = <5, 1, 3, 2, 4>
    • LIS(X) = <1, 2, 4>
  • 演算法
    1. Y <- sort(X)
    2. LCS(X, Y)
  • Time complexity
    • Θ(n2)
      • 排序:Θ(nlgn)
      • LCS:Θ(n2)

Longest Common Substring

  • Ex
    • X = <a, b, a, c>
    • Y = <a, b, c, a>
    • Output:<a, b>

c[i,j]={0,ifi=0ORj=0c[i1,j1]+1,ifX[i]=Y[j]0,ifX[i]Y[j]LCS

  • Algorithm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// X[1...n]
// Y[1...m]
lcstring = {}
length = 0
// initialize
for i <- 0 to n
c[0, i] <- 0
for i <- 0 to m
c[i, 0] <- 0

for i <- 1 to m
for j <- 1 to n {
if(X[i] != Y[i])
c[i, j] = 0
else {
c[i, j] = c[i-1, j-1] + 1
if(length < c[i, j]) {
length = c[i, j]
lcstring = X[(i-length+1)...i]
}
}
}

Matrix Chain Multiplication

  • Input:
    • n 個矩陣的大小 P[0 ... n] ( 其中Ai 的大小為 Pi1×Pi )
  • Output:
    • 算出 A1×A2××An 所需最少的純量乘法數

n 個矩陣的「Matrix chain」有 Cn1=1(n1)+1(2(n1)(n1)) 相異種乘法可能,所以列出所有乘法順序需要指數時間

  • Ex. 給定三個矩陣的大小如下
    • A110×100
    • A2100×5
    • A35×50
    • 求算出 A1×A2×A3 所需最少的純量乘法數。

若每一個矩陣均為相同大小的「方陣」,改變乘法的順序無法影響所需的純量乘法數,只能使用「Strassen's algorithm」以加速。

  • 遞迴結構
    • 令 m[i, j] 為算出 Ai××Aj 所需最少乘法數

m[i,j]={0,ifijMINikj1(m[i,k]+m[k+1,j]+Pi×Pk×Pj),ifi<j

  • 演算法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
for i <- 1 to n (「Matrix chain」長度為一)
m[i, i] <- 0
for l <- 2 to n (「Matrix chain」長度為二以上)
for i <- 1 to n-l+1 (起點)
{
j <- i+l-1 (終點)
m[i, j] <- infinity
for k <- i to j-1
{
tmp <- m[i, k] + m[k+1, j] + P_i-1 * P_k * P_j
if tmp < m[i, j]
m[i, j] <- tmp (純量乘法數)
s[i, j] <- k (切點)
}
}
  • Time complexity
    • Θ(n3)nl=2nl+1i=1i+l2k=i1
  • Space complexity
    • Θ(n2)
  • Ex
    • A13×3
    • A23×7
    • A37×2
    • A42×9
    • A59×4
    • 算出 A1×A2××A5 最少的乘法數。
matrixchaindp
martixchainproblem_2
m (乘法數) 1 2 3 4 5
1 0 63(←) 60(←) 114(↓) 156(↓)
2 0 42(←) 96(↓) 138(↓)
3 0 126(←) 128(←)
4 0 72(←)
5 0
s (切點) 2 3 4 5
1 1 1 3 3
2 2 3 3
3 3 3
4 4

最少乘法數:156

最佳乘法順序:(A1×(A2×A3))×(A4×A5)

補充例題

Example(107交通大學資料結構與演算法)

  • We define the maximum subarray of an array A to be the nonempty, contiguous subarray of A whose value have the largest sum
  • Fill in the blank (a), (b) in the following c++ function so that it returns value are placed in A[1], A[2], …, A[n-1]
1
2
3
4
5
6
7
8
9
10
11
12
13
int maxSubarray(int A[], int n) {
for(int i = 1; i<n; ++i) {
A[i] += A[i-1];
}
int ans = A[0];
int k = 0;

for(int i = 0; i<n; ++i) {
ans = max(ans, (a) );
k = min(k, (b) );
}
return ans;
}

考慮一個「Maximum subarray」為 A[x…y]

  • A[x…y] = A[1…y] - A[1…x]
  • 若欲使 A[x…y] 最大化
    • A[1…y] 必為最大
    • A[1…x] 必為最小

(a):A[i]-k

(b):A[i]