貪婪演算法 與 動態規劃演算法 短視近利與深謀遠慮 江振瑞 3.1 貪婪演算法基本概念 2 貪婪解題策略 貪 婪 演 算 法 (greedy algorithm) 使 用 貪 婪 策 略 (greedy strategy)解決問題。 假設一個問題可以藉由一系列的選擇(或決策)來解決,貪 婪演算法的特性為每一次選擇皆採取區域最佳解(locally optimal solution),而透過每一個區域最佳解最後綜合成 為全域最佳解(globally optimal solution)而將問題解決。 換句話說,貪婪演算法一步步地建構出一個問題的完整解 答。其每一步都藉由貪婪解題策略選擇當下最好的部份解 答加入完整解答中以解決問題。 3 使用貪婪解題策略的演算法 背包(Knapsack)演算法 Huffman編碼演算法 Kruskal最小含括樹演算法 Prim最小含括樹演算法 Dijkstra最短路徑演算法 4 3.2 背包演算法 5 背包演算法背景介紹 背包演算法(knapsack algorithm)使用貪婪 解題策略解決背包問題(knapsack problem) 或稱為零碎背包問題(fractional knapsack problem) 以下我們先定義背包問題 6 定義 -- 背包問題 給定一個最大載重容量(capacity)為m的背 包,以及n個可以放入背包的物品,其中第i 個物品的重量為wi>0,價格為pi>0 目標: 找出x1,…,Xn以最大化 w x i 1i n i i m 限制條件為 1in 其中 0xi1, 1 i n i p x 7 背包演算法 Algorithm 背包演算法 Input: 背包的最大容量m,以及可以放入背 包的n個物品的非負重量wi與價格pi Output: 介於0與1之間的x1,…,xn分別代表第1 個,…,第n個物品放入背包中的零碎部份。可 wixi m 。 i 以最大化 pi x,並且滿足 1i n 1i n 1: 將pi/wi由大至小排序。 2: 根據此排序來將物品依序盡可能地放入背 8 包中,直至背包容量m用完為止。 背包演算法時間複雜度 行1: 依pi/wi由大至小排序: O(n log n) 行2: 將物品依序放入背包: O(n) 總時間複雜度: O(n log n) 9 背包演算法範例 給定: n = 3, m = 5, (w1, w2, w3) = (1, 2, 3) (p1, p2, p3) = (20, 60, 45) 貪婪策略解答: p1/w1 = 20/1 = 20 p2/w2 = 60/2 = 30 p3/w3 = 45/3 = 15 最佳解: x2 = 1, x1 = 1, x3 = 2/3 最大總價值: 601+201+45(2/3)=110 10 定義 -- 0/1背包問題 給定一個最大載重容量(capacity)為m的背 包,以及n個可以放入背包的物品,其中第i 個物品的重量為wi>0,價格為pi>0 目標: 找出x1,…,Xn以最大化 w x i 1i n i i m 限制條件為 1in 其中 xi=0 or xi=1, 1 i n i p x 11 0/1背包演算法範例 給定: n = 3, m= 5, (w1, w2, w3) = (1, 2, 3) (p1, p2, p3) = (20, 60, 45) 貪婪策略解答: p1/w1 = 20/1 = 20 p2/w2 = 60/2 = 30 p3/w3 = 45/3 = 15 解答: x2 = 1, x1 = 1, x3 = 0 總價值: 601+201+450=80 最佳解: x1 = 0, x2 = 1, x3 = 1 總價值: 200+601+451=105 12 背包演算法與 0/1背包演算法範例圖示 13 3.3 Huffman編碼演算法 14 Huffman編碼 字元編碼(character coding)可以分為 固定長度編碼: 如ACSII、Unicode 可變長度編碼: Huffman code Huffman編碼以字首碼(prefix code)方式達到 字元編碼最佳資料壓縮(optimal data compression) 字首碼 (prefix code): 任何字元編碼一定不是其他 字元編碼的字首(prefix)。 可以使用二元樹來呈現,達到簡單編碼(encoding) 15 與解碼(decoding)的功能。 Huffman編碼範例 假設給定一個僅用到a, b, c, d, e五個字元的文件, 現在欲針對五個字元進行編碼,以下是可能的固定 長度編碼與可變長度的Huffman字首碼。 字首碼讓出現頻率較高字元的編碼較短,以達到使 用最少位元就可以將所有資料儲存的目標。 a b c 14% 17% 23% 6% 40% 固定長度編碼 000 001 010 011 100 可變長度編碼 1111 110 10 1110 0 出現頻率 d e 16 對應不同編碼的樹及其成本 樹T的成本( 單一字元編碼成本) Cost(T) f (c)d T (c) cC Cost(T)=3 Cost(T)=2.17 17 Huffman編碼演算法 Algorithm Huffman編碼演算法 Input: 字元集合C與每個字元的出現頻率f Output: Huffman編碼樹 1. 2. 3. 4. 5. 6. 7. 8. 9. n |C| //C: the set of n characters Q C //Q: 優先佇列,以字元頻率為優先次序 for i 1 to n – 1 //n個字元(節點)欲合併成一個節點,每迭代合併一次可少一節點 配置一個新的樹節點u u.left x GetMin(Q) u.right y GetMin(Q) u.f x.f + y.f Insert u into Q return GetMIN(Q) 作為Huffman編碼樹的樹根 18 Huffman編碼演算法時間複雜度 行2: O(n)建立優先佇列Q 行3-8: for迴圈一共執行n-1次,而且迴圈中 的優先佇列操作均為O(log n)複雜度,因此 整個迴圈具有O(n log n)的複雜度 總時間複雜度: O(n log n) 19 Huffman編碼演算法的執行範例 20 Huffman編碼演算法的執行範例(續) 21 Huffman編碼演算法的執行範例(續) 22 Huffman編碼演算法的執行範例(續) 23 Huffman編碼演算法的執行範例(續) (4) 24 3.4 Kruskal最小含括樹演算法 25 最小含括樹 最小含括樹(Minimum Spanning Tree, MST)可以定義在歐式空間(Euclidean space) 或者一個圖(graph)上。 給定一個加權連通無向圖(weighted connected undirected graph) G = (V, E) 含括樹(spanning tree) H= (V, T), T E, 是 一個無向樹(undirected tree),它是G的子圖, 包含G的所有節點 最小含括樹MST是一個擁有最小(minimum)總 權重(weight)或總成本(cost)的含括樹。 26 最小含括樹範例 圖G的最小含括樹(非唯 一) 一個圖G 27 Kruskal最小含括樹演算法概念 Kruskal最小含括樹演算法是一個貪婪演算 法(greedy algorithm) 它採取貪婪解題策略產生給定圖G=(V, E) 的最小含括樹H=(V, T),每次都是挑選最 小成本且不形成cycle的邊加入目前的最小 含括樹的邊集合T之中 因為n個節點的樹具有n-1個邊,因此,經 過n-1次邊的挑選之後,就可以形成累積 成本最小的含括樹。 28 Kruskal最小含括樹演算法 Algorithm Kruskal最小含括樹演算法 Input: 無向加權圖G=(V, E),其中|V|=n Output: G的最小含括樹(MST)H=(V, T) 1. T← //T為MST的邊集合,一開始設為空集合 2. while T包含少於n-1個邊 do 3. 選出邊(u, v),其中(u, v)E,且(u, v)的加權(weight)最小 4. E←E-(u, v) 5. if ( (u, v)加入T中形成循環(cycle) ) then 將(u, v)丟棄 6. else T←T(u, v) 7. return H=(V, T) 29 Kruskal最小含括樹演算法執行範例 30 Kruskal最小含括樹演算法討論 Q: 我們如何檢查加入新的邊是否會形成循環? A: 使用 集合 (SET) 尋找與 聯集 (UNION)操作 使用 集合 (SET) 與 聯集 (UNION)操作。 考慮樹的節點集合: 一開始產生n個包含單一節點的集合;也就是說若 V={v1,…,vn} ,則產生{v1}, {v2},…,{vn} 加入邊(u, v)是否會形成循環: 找出u,v所屬的集合,若 u, v 在相同的集合, 則加入邊(u, v) 會形成循環。反之,若uS1 , vS2 ,而且 S1S2則加入邊(u, v)不會形成循環,此時應對 S1 與 S2 進行聯集操作。 31 Kruskal演算法的時間複雜度 時間複雜度: O(|E| log|E|) 排序 : O(|E| log|E|) 行2-6 迴圈 (幾乎每個邊都要檢查) O(|E|) 找出元素所在的集合 O(log |V|) 聯集兩集合 O(log |V|) : O(|E| log |V|) O(|E| log|E|) |E| |V|2 =O(|V|2 log |V|) =O(n2 log n) 32 3.5 Prim最小含括樹演算法 33 Prim最小含括樹演算法概念 Prim最小含括樹演算法是一個貪婪演算法(greedy algorithm)。 它採取貪婪解題策略產生給定圖G=(V, E)的最小含 括樹H=(V, T)。此演算法先隨意挑一個節點加入集 合X中,此後每次都挑選一個一端的節點在X中, 而另一端的節點在(V-X)中的最小成本的邊。如此, 可保證將所挑選的邊加入T之後不會形成循環 (cycle),這代表H=(V, T)是一棵樹(tree)。 等挑完n-1個邊之後,H=(V, T) 就是最小含括樹 (MST)。 34 Prim最小含括樹演算法 Algorithm Prim最小含括樹演算法 Input: G=(V, E)為無向加權圖,其中|V|=n Output:G的最小含括樹(MST)H=(V, T) 1. T← //T為MST的邊集合,一開始設為空集合 2. X←{v} //隨意選擇一個節點v加入集合X中 3. while T包含少於n-1個邊 do 4. 選出(u, v)E,其中uX且vV-X,且(u, v)的加權(weight)最小 5. T←T(u, v) //(u, v)是一個邊 6. X←X{v} 7. return H=(V, T) 35 Prim最小含括樹演算法執行範例 36 Prim最小含括樹演算法時間複雜度 總時間複雜度: O(n2),因為 外層的while迴圈(行3-6): n-1 O(n) 內層迴圈(行4): 在(u, v)中選擇最小權 重,其中u屬於X,v屬於V-X O(n) (藉著使用Prim提出的兩個向量C1和C2) (Ref: R. C. Prim, “Shortest connection networks and some generalizations,” Bell System Technical Journal, 36(1389–1401), 1957.) 比較: 如果 |E|<<n2 ,則採用Kruskal演算法 (複雜度O(|E| log|E|)效能較佳 37 3.6 Dijkstra最短路徑演算法 38 圖的最短路徑 由圖(graph)中的某個節點(vertex or node)v到圖中的另一節 點u,若v到u之間存在一條路徑(path),則路徑中所經過的 邊(edge)的權值(weight)的總合稱為路徑的成本(cost)或距離 (distance)。所有路徑中具有最小成本的稱為最短路徑 (shortest path)。 由於最短路徑具有許多應用,因此有許多求取最短路徑的 演算法,著名的演算法包括: (1) Dijkstra演算法(使用貪婪解題策略) (2) Bellman-Ford演算法(使用動態規劃解題策略) (3) Floyd-Warshall演算法(使用動態規劃解題策略) 39 Dijkstra最短路徑演算法設計者 E. W. Dijkstra(1930年5月11日-2002 年8月6日)生於荷蘭鹿特丹 在1972年獲得圖靈獎(Turing Award) 2002 年 , Dijkstra 獲 得 了 ACM PODC (Principles of Distributed Computing) 最具影 響力 論文 獎(Influential Paper Award) , 以 表 彰 他 在 分 散 式 計 算 (distributed computing)領域中關於自 我穩定(self stabilization)計算模式的 貢獻。為了紀念他,這個每年一度獎 項 也 在 此 後 被 更 名 為 Dijkstra 獎 (Dijkstra Prize) Source: http://en.wikipedia.org/wiki/Edsger_W._Dijkstra Creative Commons Attribution-Share Alike 3.0 Unported Author:Hamilton Richards 40 Dijkstra最短路徑演算法 是喝咖啡時20分鐘想出的發明 “One morning I was shopping in Amsterdam with my young fiancée, and tired, we sat down on the café terrace to drink a cup of coffee and I was just thinking about whether I could do this, and I then designed the algorithm for the shortest path. As I said, it was a 20-minute invention. In fact, it was published in 1959, three years later.” Thomas J. Misa (Editor), "An Interview with Edsger W. Dijkstra," Communications of the ACM 53 (8): 41–47, 2010. Attribution 2.0 Generic (CC BY 2.0) Elliott Brown Source: https://www.flickr.com/photos/ell-rbrown/14165662691/in/photolist- 41 Dijkstra最短路徑演算法介紹 Dijkstra演算法: Dijkstra演算法屬於求取單一(single)源 (source)節點至全部(all)終(destination)節點的單源點至全終 點之一至全(one-to-all)最短路徑演算法。 Dijkstra演算法只能用在所有的邊都是非負邊(non-negative weighted edge)的圖。因為負邊有可能產生負循環,因而無 法產生正確的最短路徑,而Dijkstra演算法並無法檢查給定 的圖是否有負循環。 Dijkstra最短路徑演算法採用貪婪策略解決問題,每次都挑 選一個目前可以由源節點抵達距離(累積邊加權)最小的節點 往外調整其鄰居節點的最短路徑距離。在經過n次(n為節點 個數)的節點選擇之後,則所有的節點都可以求得由單一源 節點可以抵達的最短路徑距離。 42 Dijkstra最短路徑演算法 Algorithm Dijkstra最短路徑演算法 Input:給定一個非負加權有向圖(non-negative weighted digraph)G=(V, E),及一個 來源(source)節點s。G各邊的加權值以w[x][y]表示,其中x 及y為邊的二個節點。 Output:對每一個節點u而言,傳回一個由s到u的最短路徑距離(累積邊加權)d[u]。 1. d[s]←0; d[u]←∞ for each u≠s 2. 將每一個節點加入優先佇列Q 3. while Q≠ do 4. 自Q中移出具有最小d[u]值之節點u 5. for 每一個與u相鄰之節點x do 6. if d[x] > d[u]+w[u][x] then 7. d[x]←d[u]+w[u][x] 8. return d 43 Dijkstra最短路徑演算法 如何記錄所有的路徑? 我們將d[u]以加中括號的方式標記在每一個節點旁,使用下圖 說明Dijkstra演算法求節點A到每一個節點最短路徑的過程。 若要讓Dijkstra演算法也能夠求出每一條最短路徑所經過的每 一個節點,則我們要將每一節點在最短路徑中的前一節點紀 錄下來,其作法為增加一個陣列p(代表predecessor,前行者) 來記錄最短路徑中的每一個節點的前一節點。並將Dijkstra演 算法之if敘述修改如下: if (d[x] > d[u]+w[u][x]) then d[x]←d[u]+w[u][x] p[x]←u //此敘述為新加入者,代表在最短路徑中節點x的前一節點為u 44 Dijkstra演算法執行範例 45 Dijkstra最短路徑演算法複雜度 假設G一共有n個節點,m個邊(也就是|V|=n, |E|=m) 行2將每一個節點加入優先佇列Q,因此Q具有n個元素 行3的while迴圈每次迭代會自Q中次移出一個節點,因此會 執行n次迭代 行4使用O(log n)時間自Q中移出最小d[u]值之節點u 行5的for迴圈在整個演算法的執行過程中一共執行m次迭代 行7使用O(log n)的時間根據新的d[u]值更新u在Q中的位置( 先刪除u在新增u 因此總時間複雜度為O((n+m) log n)=O( (|V|+|E|) log |V|) 46 3.7 動態規劃演算法基本概念 47 動態規劃解題策略 動態規劃演算法(dynamic programming algorithm)使用動態 規劃策略(dynamic programming strategy)解決問題。它將 原問題分解成一系列子問題(subproblems),並依序解決子 問題來解決原問題。為避免一再地解重複的子問 題,一旦解 出子問題的解答(solution),即會將其存在表格(或陣列)中。 當需要用到某一子問題的解答時,即直接從表格中取出其解 答以節省計算時間,是一個「系統化」的、「節省不必要計 算」的、「以空間換取時間」的演算法。 一個動態規劃演算法一般先解出最簡單的子問題,並以一定 的程序持續運行直至求出原問題解答為止。 48 最佳解原則 最佳解原則(Principle of Optimality): 假設我們可以將一個問題P分解為一系列的子問題 P1 , P2, …, Pn-1, Pn,而必須作出一系列的決策 D1, D2, …, Dn-1, Dn 若這一系列的決策 D1, D2, …, Dn-1, Dn可以產生P的 最佳解,則針對於完成第i個到第j個(1ijn)連續決 策後所產生的新狀態而言(也就是在新狀態解決子問 題P1,…,Pi-1,Pj+1,…,Pn) ,其他的決策相依於這個狀態 必定也能產生最佳解。 49 動態規劃與貪婪演算法之比較 比較: 二者都是透過一系列的決策以解決問題,但是有 以下的不同點: 在貪婪演算法中,任何決策都是獨立 (independent)的,都只要考慮區域最佳解(locally optimal)。這些區域最佳解最後會加成為全域最佳 解(globally optimal solution)。 在動態規劃演算法中,決策是相依的 (dependent)。相依於第i個到第j個(1ijn)決策所 產生的狀態(子問題)而言,其他的決策必定也是最 佳的,藉以求得全域最佳解(globally optimal 50 使用動態規劃解題策略的演算法 Bellman-Ford最短路徑演算法 Floyd-Warshall最短路徑演算法 多階圖最小成本路徑演算法 最長共同子序列演算法 矩陣鏈乘積演算法 51 3.8 Bellman-Ford 最短路徑演算法 52 Bellman-Ford最短路徑演算法 介紹 與Dijkstra演算法相同,Bellman-Ford演算 法也是屬於求取單一源節點至全部終節點 的一至全最短路徑演算法。 但是與Dijkstra演算法不同的是,BellmanFord演算法可以檢查圖是否有負加權循環 (cycle) , 因 此 在 具 有 負 加 權 (negative weight)邊的圖也可以正確的執行。 53 Bellman-Ford最短路徑演算法 介紹(續) Bellman-Ford最短路徑演算法採用動態規劃策略解 決問題。一開始在第1次迭代先求出所有屬於1-邊 路徑(1-edge path)的最短路徑,並將其最短路徑距 離儲存在陣列中;然後基於這個儲存結果在第2次 迭代針對每個邊,由始點(starting node)往外調整到 止點(ending node)的最短路徑距離,可以得出所有 屬於2-邊路徑(2-edge path)的最短路徑;…。依此 類推則在第n-1次迭代可以求出所有屬於(n-1)-邊路 徑((n-1)-edge path)的最短路徑。因為具n個節點的 圖最長的路徑具有n-1個邊,因此第n-1次迭代求出 的路徑已經是最終的正確結果了。 54 Bellman-Ford最短路徑演算法 Algorithm Bellman-Ford最短路徑演算法 Input: 給定一個加權有向圖(weighted digraph)G=(V, E),及一個來源(source)節點s。G各 邊的加權值以w[x][y]表示,其中x 及y為邊的二個節點。 Output: 對每一個頂點u而言,傳回一個由s到u的最短路徑距離(累積邊加權)d[u]。 1. d[s]←0; d[u]←∞ for each u≠s 2. for i←1 to |V|-1 do 3. for 每一個G的邊(u, x) do 4. if d[x] > d[u] + w[u][x] then 5. d[x]← d[x] + w[u][x] 6. for 每一個G的邊(u, x) do //檢查有無負循環(negative-weight cycle) 7. if d[x] > d[u] + w[u][x] then return false //代表有負循環,無法產生正確結果 8. return d 55 Bellman-Ford最短路徑演算法 複雜度 假設G一共有n個節點,m個邊(也就是|V|=n, |E|=m) 行2-5的外層for迴圈一共有n-1次迭代 行3-5的內層for迴圈一共有m次迭代 行4-5為內層if指令,針對每個邊(u, x)依據目前的d[u]值調 整d[x] 行6-7的for迴圈在求出(n-1)邊路徑之後再針對每個邊(u, x) 依據目前的d[u]值調整d[x],若有任何調整產生則表示有一 個n邊路徑(也就是循環) 因此總時間複雜度為行2-5的外層for迴圈n-1次迭代次數與 行3-5的內層for迴圈m次迭代相乘得O(n m)= O(|V| |E|) 56 Bellman-Ford最短路徑演算法 執行範例 57 3.9 Floyd-Warshall 最短路徑演算法 58 Floyd-Warshall最短路徑演算法 介紹 與Dijkstra演算法與Bellman-Ford演算法不同的是, Floyd-Warshall演算法可以求出全部節點配對的最 短路徑,是一個全配對最短路徑(all-pair shortest path)演算法。 Floyd-Warshall演算法可以處理有負邊的圖,但是 不能用以檢查有負迴圈的圖。 59 Floyd-Warshall最短路徑演算法 介紹(續) Floyd-Warshall演算法採用動態規劃策略解決問題,利 用一個n×n(n為節點總數)的二維陣列d來記錄每一節點 配對間的最短路徑成本或距離(distance) 。 在啟始(initial)狀況時, d[i][j]=w[i][j],for each i and j。 (w[i][j]=0,for i=j; w[i][j]=, for (i, j); w[i][j]=the weight of (i, j) for (i, j) E) Floyd-Warshall演算法執行時會不斷的更新陣列d。在第 k次更新陣列d時,表示d中所紀錄的最短路徑是經由編 號小於或等於k的節點當作中間節點所造成的。因此, 當第n次更新陣列d時,則表示d中所紀錄的最短路徑是 可以經由所有節點當作中間節點所造成的,這也就是 演算法所需要的結果。 60 Floyd-Warshall最短路徑演算法 Algorithm Floyd-Warshall最短路徑演算法 Input:G為一個加權圖有向(weighted digraph), G中各邊的加權值以w[x][y]表示 ,x 及y為邊的二個頂點。 Output:G中的每一個節點配對的最短路徑距離d[x][y],及對應的路徑前節點 p[x][y],其中x及y為邊的二個節點 1. d[i][j]=w[i][j],for each i and j 2. for(k←1 to n) do //假設節點的編號由1至n 3. for(i←1 to n) do 4. for(j←1 to n) do 5. if (d[i][j]>d[i][k]+d[k][j]) 6. d[i][j]←d[i][k]+d[k][j] 7. p[i][j]←p[k][j] 8. return d 61 Floyd-Warshall演算法討論 可以使用一個前節點陣列(predecessor matrix)p紀錄每個節點在最短路徑上的前 節點。初始化p[i,j]時,若i=j或(i,j)∉E則初 始為NIL(),否則初始為i。 等執行完演算法後,則可藉由前節點陣列 來建立出由任意節點到其他任意節點的最 短路徑。 62 Floyd-Warshall最短路徑演算法 範例: 陣列初始化 G(V, E) s a b c d s 0 a 6 0 2 1 b 2 0 3 6 c 3 4 0 d 1 5 0 d[i][j] s a b c d s a s c d b a c d c s a d B c p[i][j] 63 Floyd-Warshall最短路徑演算法 複雜度 假設G一共有n個節點(也就是|V|=n) 行2的外層for迴圈一共有n次迭代 行3的中層for迴圈一共有n次迭代 行4的內層for迴圈一共有n次迭代 行5-7的if敘述的執行為常數時間 因此總時間複雜度為O(n3)=O(|V|3) 64 3.10 多階圖最小成本路徑演算法 65 多階圖最小成本路徑問題 (multi-stage graph minimum-cost path problem) 多階圖G=(V,E) 是有向圖(directed graph),其節點 被分割成 k2 個互斥集(disjoint sets) Vi, 1 i k。 此外,若<u,v>是E的邊,則uVi 且 vVi+i, 1 i <k,每個邊都有一個加權(weight)wi,i+1 (或稱為成 本或距離)。 其中集合 V1 和 Vk 都僅包含一節點(node or vertex)。 多階圖問題是要找出從 V1 中的源點(source)s 到 Vk 中的標點(target) t 的最小成本路徑(minimum-cost path)。 每一個集合 Vi 被定義為圖中的階(stage)。 66 貪婪演算法無法解決 多階圖最小成本路徑問題 例:從多階圖(multi-stage graph)中找出v0到v3的最短路徑。 貪婪演算法: v0v1,2v2,1v3 = 23 最佳解: v0v1,1v2,2v3 = 7 貪婪演算法無法解決此問題,這是因為不同階(stage)的決 策會影響到其他階的決策。 67 貪婪演算法無法解決 多階圖最小成本路徑問題(續) 4 A 例如: D 1 11 S 2 5 B 18 9 E 16 13 T 2 5 C 2 F 貪婪演算法無法解決此問題: S A D T 1+4+18 = 23. 最短路徑為: S C F T 5+2+2 = 9. 就像分期買商品一樣,都分三期付款,最終都可以得到商品的 產權。有的付款方式第一期要繳1萬,有的要繳2萬,有的要繳5 萬。但是依照不同的第一期繳法,則在第二期甚或第三期都有 不同的繳款選擇,而造成繳款總額的不同。 68 動態規劃 動態規劃方法: 1 S 2 B 5 A C d(A, T) d(B, T) T d(C, T) d(S, T) = min{1+d(A, T), 2+d(B, T), 5+d(C, T)} 69 動態規劃 4 A D 1 A 4 11 11 D E d(D, T) S T d(E, T) 2 5 B 18 9 E 16 13 T 2 5 C 2 F d(A, T) = min{4+d(D, T), 11+d(E, T)} = min{4+18, 11+13} = 22. 70 動態規劃 d(B, T) = min{9+d(D, T), 5+d(E, T), 16+d(F, T)} = min{9+18, 5+13, 16+2} = 18. d(C, T) = min{ 2+d(F, T) } = 2+2 = 4 d(S, T) = min{1+d(A, T), 2+d(B, T), 5+d(C, T)} = min{1+22, 2+18, 5+4} = 9. 4 A D 1 11 S 2 9 5 B E 16 13 T B 5 D E d(D, T ) d(E, T ) T 2 5 C 9 18 2 F 16 F d(F, T ) 71 解決多階圖最小成本路徑問題的 動態規劃演算法 Algorithm 多階圖最小成本路徑演算法 Input:具n個頂點(vertices)的k階多階圖G(V, E), V= i=1..k Vi , ViVj= for ij, V1={1},Vk={n}, <x,y>E (xVi yVi+i), <x,y>的權重為w[x,y] Output: 具k個節點由節點1到節點n的最小成本路徑p[1..k],成本為d[1] 1. d[n]=0; d[1..n-1]=; //陣列d儲存節點到標點t的最小距離(distance) 2. for jk-1 to 1 do 3. for every node x in Vj do 4. for every edge <x, y>E do 5. if (d[x]>w[x,y]+d[y]) do 6. d[x]=w[x,y]+d[y] 7. s[x]=y //s代表節點的下節點(successor) 8. p[1]=1;p[k]=n; 9. for j2 to k-1 do p[j]s[p[j-1]]; 10. return p,d[1] 72 3.11 最長共同子序列演算法 73 最長共同子序列 以下我們說明最長共同子序列(Longest common subsequence, LCS or LCSS)相關背景知識 令X為一個由若干符號依序排列組成的序列(sequence),則X 的子序列(subsequence)為從X刪除0個或多個符號(不必要為 連續性的)的序列 例: 令X = b a c a d,則ad, ac, bac, acad, bacad, bcd等與 空序列都是A的子序列 例: 序列X = b a c a d 與 Y = a c c b a d c b 的共同子序列 (common subsequence)有: ad, ac, bac, acad等 例: 序列X與Y的最長共同子序列(longest common subsequence)為: a c a d 74 最長共同子序列應用: DNA序列比對 DNA = {A|C|G|T}* (A: 腺嘌呤; C: 胞嘧啶; G: 鳥嘌呤; T: 胸腺嘧啶) S1=ACCGGTCGAGTGCGGCCGAAGCCGGCCGAA S2=GTCGTTCGGAATGCCGTTGCTGTAAA Q: S1與S2是否為相似的DNA序列? A: 這問題可以由找出S1與S2的最長共同子序列 來解決。 75 最長共同子序列應用: 化身路徑群組 化身路徑群組(Avatar Path Clustering): 由於相似的個性,興趣或習慣,網路虛擬環境(Networked Virtual Environment, NVE)或巨量多 人線上遊戲(Massively Multiplayer Online Game, MMOG)的用戶或化身(avatar)可能具有相似的 行為模式,導致在虛擬世界中有著類似的化身路徑。 我們希望將類似的化身歸類為一個群組,並為他們找到代表性路徑(representative path, RP)。 參考論文: Jehn-Ruey Jiang, Ching-Chuan Huang, and Chung-Hsien Tsai, “Avatar Path Clustering in Networked Virtual Environments," in Proc. of the 4th International Workshop on Peer-to-Peer Networked Virtual Environments (P2PNVE 2010), 2010. 下圖來源: Huiguang Liang, Ransi Nilaksha Silva, Wei Tsang Ooi, Mehul Motani, “Avatar mobility in user-created networked virtual worlds: measurements, analysis, and implications,” Multimedia Tools and Applications, v.45 n.1-3, p.163-190, October 2009. 76 最長共同子序列應用: 化身路徑群組(續) 在第二人生(Second Life, SL)贈品島(Freebies Island)的兩條路徑有多相似? 77 最長共同子序列應用: 化身路徑群組(續) 將化身路徑轉為序列: 將虛擬世界切割為方格 (grid cell),並針對化身 路徑每隔固定時間取樣, 找出其所在方格編號形成 序列。 SeqA:C60.C61.C62.C63.C55.C47.C39.C31.C32 78 最長共同子序列應用: 化身路徑群組(續) SeqA :C60.C61.C62.C63.C55.C47.C39.C31.C32 SeqB :C60.C61.C62.C54.C62.C63.C64 LCSSAB :C60.C61.C62. C63 找出兩條路徑對應的 最長共同子序列以衡 量其相似程度。 79 最長共同子序列問題 以下我們定義最長共同子序列(Longest Common Subsequence, LCS)問題: 給定兩個序列X = <x1,x2,...,xm>, Y = <y1,y2,...,yn>,找出X和Y的最長共同子序列長 度 80 最長共同子序列問題 暴力法時間複雜度 產生序列X(或Y)的所有子序列,然後檢查每 個子序列是否也是序列Y(或X)的子序列,然 後儲存下最長的子序列並輸出。複雜度為: n * 2m = O(2m) 或 m * 2n = O(2n ) Q1: 如何產生一個序列的所有子序列? Q2: 如何檢查一個序列是否為另一個序列的 81 子序列? 最長共同子序列問題 子問題的遞迴關係 我們定義Xi = < x1,x2,...,xi >及Yj = <y1,y2,...,yj>。 令c [i, j]是Xi 和Yj 的LCS的長度,則我們有以下 遞迴關係: if i=0 or j=0 0 if i,j>0 and xi=y c[i, j] c[i -1, j -1] +1 max{c[i, j -1], c[i -1, j]} if i,j>0 and x y j i 82 j 最長共同子序列演算法 Algorithm 最長共同子序列演算法 Input: 兩個序列X = <x1,x2,...,xm>, Y = <y1,y2,...,yn> Input: X和Y的最長共同子序列長度 1 m length[X] 2 n length[Y] 3 for i 1 to m do 4 c[i, 0] 0 5 for j 1 to n do 6 c[0, j] 0 83 7 for i 1 to m do 8 for j 1 to n do 9 if xi = yj then 10 c[i, j] c[i-1, j-1]+1 11 b[i, j] 0 “” //for both i-1 and j-1 12 else if c[i–1, j] c[i, j-1] then 13 c[i, j] c[i-1, j] 14 b[i, j] “” //for i-1 15 else c[i, j] c[i, j-1] 16 b[i, j] “” //for j-1 only 17 return c and b 84 最長共同子序列演算法 執行範例 X=ABCBDAB Y=BDCABA 85 最長共同子序列演算法 時間複雜度 行7的外層for迴圈一共有m次迭代 行8的內層for迴圈一共有n次迭代 行9-16的if敘述需要常數時間 因此總時間複雜度為O(mn) ,而非暴力法的 O(2m) 或 O(2n) 86 最長共同子序列演算法 如何找出最長共同子序列 PRINT_LCS(b, X, i, j ) 1 if i = 0 or j = 0 then 時間複雜度: O(m+n) 2 return 3 if b[i, j] = “” then 4 PRINT_LCS(b, X, i-1, j-1) 5 print xi 6 else if b[i, j] = “” then 7 PRINT_LCS(b, X, i-1, j) 8 else PRINT_LCS(b, X, i, j-1) 藉由呼叫PRINT_LCS(b, X, length[X], length[Y])函數來印出 LCS 87 3.12 矩陣鏈乘積演算法 88 矩陣鏈乘積 (matrix-chain product) Q: 如何以最少的純量(scalar)乘法,算出 A1…An的矩陣鏈乘積? A: 加上括號指定計算矩陣乘積最佳順序 舉例: A1 A2 A3 A4 ( A1( A2 ( A3 A4 ))) ( A1(( A2 A3 ) A4 )) (( A1 A2 )( A3 A4 )) (( A1( A2 A3 )) A4 ) ((( A1 A2 ) A3 ) A4 ) 89 二個矩陣相乘的演算法 MATRIX MULTIPLY(A,B) 1 if columns[A] rows[B] 2 then error “不相容的矩陣大小” 3 else for i 1 to rows[A] 4 for j 1 to columns[B] C[i, j ] 0 5 6 for k 1 to columns[A] C[i, j ] C[i, j ] + A[i, k ]B[k , j ] 7 8 return C 90 二個矩陣相乘時間複雜度: 假設 A 是一個 p q 的矩陣,B 是一個 q r 的矩陣, 那個 A x B 的時間複雜 度為 p q r 。 91 矩陣相乘執行順序非常關鍵 假設 A1 是個10 100 的矩陣, A2 是一個100 5 的矩陣, A3 是一個 5 50 的矩陣。 那麼算出 (( A1 A2 ) A3 ) 需要 10 100 5 + 10 5 50 7500 次的純量相乘。 然而算出 ( A1( A2 A3 )) 卻需要 100 5 50 + 10 100 50 75000 次的純量相乘。 92 矩陣鏈乘積問題 (matrix-chain product problem) 給定一長度為n的矩陣鏈 A1 , A2 ,..., An , 對於i=1,2,…,n而言,矩陣Ai 的維度為 pi-1pi。找出一種方式對 A1 A2 ... An 進行 完全括號(fully parenthesized)以最少的 純量乘法求出矩陣鏈乘積。 若一個矩陣鏈乘積為完全括號,則其包 含單一矩陣或為兩個完全括號矩陣鏈乘 積的相乘。 93 矩陣鏈乘積問題 窮舉所有不同的括號方式: 不同括號方式總數相當於將矩陣鏈在第k個矩陣之 後與第k+1個矩陣之前,使用括號分為二組後再計 算其結果,而1kn-1。我們可得: 1 if n 1 P(n) n -1 P(k)p(n - k) if n 2 k 1 實際上,P(n)為卡塔蘭數(Catalan number)=Cn-1 =O(2n) 94 卡塔蘭數(Catalan number) 卡塔蘭數是以比利時的數學家歐仁查理 卡塔蘭(Eugène Charles Catalan, 1814– 1894)命名。 卡塔蘭數的一般項公式為 n 2 n 1 4 Cn n + 1 n ( n3/ 2 ) 95 矩陣鏈乘積演算法解題思維 步驟1:找出子問題切割方式 Optimal (( A1 A2 ... Ak )( Ak +1 Ak + 2 ... An )) Combine 96 矩陣鏈乘積演算法解題思維步 驟 2: 找出子問題遞迴關係 定義 m[i, j]= 計算 Ai.. j Ai Ai+1.. A j 所需的 最小相乘數。 目標為求得 m[1, n] 0 i j m[i, j] min i k j{m[i, k] + m[k + 1, j] + p i -1p k p j} i j 97 矩陣鏈乘積演算法解題思維步 驟 3: 以表格儲存計算過的資訊 與其一再地遇到相同問題而重複遞迴求 解,取而代之地我們利用一表格化的 (tabular)、由下而上(bottom-up)的方式 來計算最低成本。 過程利用一輔助表格m[1..n, 1..n] 來紀 錄最小成本m[i, j] ,並利用另一個輔助 表格s[1..n, 1..n]來記錄哪一個指標 k 造 就了m[i, j]的最小成本。 98 矩陣鏈乘積演算法 MATRIX_CHAIN_ORDER MATRIX_CHAIN_ORDER(p) 1 n length[p] –1 2 for i 1 to n 3 do m[i, i] 0 4 for l 2 to n 5 do for i 1 to n – l + 1 6 do j i + l – 1 7 m[i, j] 8 for k i to j – 1 9 do q m[i, k] + m[k+1, j]+ pi-1pkpj 10 if q < m[i, j] 11 then m[i, j] q 12 s[i, j] k 13 return m and s 時間複雜度: 3 O( n ) 99 例子: A1 A2 30 35 p0 p1 35 15 p1 p2 A3 15 5 A4 5 10 A5 10 20 A6 20 25 p2 p3 p3 p4 p4 p5 p5 p6 100 在n=6時, MATRIX-CHAIN-ORDER所計算出的 m 與 s 表格 101 m[2,5]= min{ m[2,2]+m[3,5]+p1p2p5=0+2500+351520=13000, m[2,3]+m[4,5]+p1p3p5=2625+1000+35520=7125, m[2,4]+m[5,5]+p1p4p5=4375+0+351020=11374 } =7125 102 印出最佳括號方式 PRINT_OPTIMAL_PARENS(s, i, j) 1 if i=j 2 then print “A”i 3 else print “(“ 4 PRINT_OPTIMAL_PARENS(s, i, s[i,j]) 5 PRINT_OPTIMAL_PARENS(s, s[i,j]+1, j) 6 print “)” 例: (( A1( A2 A3 ))(( A4 A5 ) A6 )) 103 The End 104