Alg3(NoMark)2014-1106

advertisement
貪婪演算法
與
動態規劃演算法
短視近利 與 深謀遠慮
國立中央大學
資訊工程學系
江振瑞 教授
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
1i  n
i
i
m
限制條件為 1in
其中 0xi1, 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,並且滿足
1i  n
1i  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
最大總價值: 601+201+45(2/3)=110
10
定義 -- 0/1背包問題



給定一個最大載重容量(capacity)為m的背
包,以及n個可以放入背包的物品,其中第i
個物品的重量為wi>0,價格為pi>0
目標: 找出x1,…,Xn以最大化
w x
i
1i  n
i
i
m
限制條件為 1in
其中 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
總價值: 601+201+450=80
最佳解: x1 = 0, x2 = 1, x3 = 1
總價值: 200+601+451=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)
cC
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)
會形成循環。反之,若uS1 , vS2 ,而且 S1S2則加入邊(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,其中uX且vV-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最短路徑演算法
如何記錄所有路徑經過的節點?

若要讓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
動態規劃解題策略(續)
一般動態規劃演算法的設計原則如下:
 決定需要建構的表格
 決定遞迴關係
 決定填表格順序,用以填完表格而解決問題

一 般 依 照 遞 迴 關 係 中 的 邊 界 條 件 (boundary
condition)將初始值(initial value)填入表格,然
後以由底而上(bottom-up)的方式持續運行直至
填完整個表格而求出原問題解答為止。
49
動態規劃與貪婪演算法之比較
比較:
二者都是透過一系列的決策以解決問題,但是有以下的
不同點:


在貪婪演算法中,任何決策都是獨立(independent)的,都
只要考慮區域最佳解(locally optimal)。


而這些區域最佳解最後可以匯總為全域最佳解 (globally optimal
solution)。
在動態規劃演算法中,決策是相依的(dependent),也就是
說每個決策不能只是考慮區域最佳解。

若需要執行n次決策,則第i次決策與第1次到第i-1次連續決策所產
生的狀態是相依的;或是說第i次決策與第i+1次到第n次連續決策所
產生的狀態是相依的。
50
使用動態規劃解題策略的演算法






多階圖最小成本路徑演算法
0/1背包演算法
最長共同子序列演算法
矩陣鏈乘積演算法
Bellman-Ford最短路徑演算法
Floyd-Warshall最短路徑演算法
51
3.8
多階圖最小成本路徑演算法
52
多階圖最小成本路徑問題簡單範例
輸入: 一個多階圖
輸出: 節點S到節點T的
最小成本路徑



就像分三期付款以取得商品的產權一樣。有的付款方式第一期要
繳1萬,有的要繳3萬,有的要繳6萬。但是依照不同的第一期繳
法,則在第二期及第三期有不同的繳款選擇,而造成繳款總額的
不同。我們當然想要選擇最少繳款總額的方式。
貪婪演算法無法解決此問題: S A D T  cost = 1+2+17 = 20.
實際最短路徑: S C F T  cost = 6+3+1 = 10.
53
多階圖定義



一個k階(k-stage),k2,多階圖(multi-stage graph)
G=(V,E)是一個加權有向圖(weighted digraph),其節
點被分割置入k個互斥集合(disjoint sets) V1, V2, …,Vk。
其中每一個集合Vi, 1 i  k, 被定義為圖中的第i階(ith
stage),而且V1 和 Vk 都僅包含一個節點(node or
vertex)。V1 中的節點稱為源點(source),而Vk中的節
點稱為標點(target或destination)。
此外,若<u,v>是E的邊,則uVi 且 vVi+1,1 i <k,
而每個邊都有一個加權(weight)(或稱為成本或距離)。
54
多階圖範例

以下是一個4階多階圖
55
多階圖最小成本路徑問題
多階圖最小成本路徑問題(multi-stage graph
minimum-cost path problem)定義:

給定一個k階多階圖G=(V,E),找出從 V1 中的源點
(source) v1(或s) 到 Vk 中的標點(target) vk(或t)
的最小成本路徑(minimum-cost path)。
56
多階圖最小成本路徑問題範例




範例:從以下多階圖(multi-stage graph)中找出v1 到v4 的
最短路徑
貪婪演算法解答: v1v2,2v3,1v4  cost = 1+1+29=31
最佳解: v1v2,3v3,4v4  cost = 6+3+1=10
貪婪演算法無法解決此問題,這是因為不同階(stage)的決
策會影響到其他階的決策,它們是相依的。
57
使用動態規劃演算法解決
多階圖最小成本路徑問題

決定表格結構: 我們首先決定使用陣列d[x]來儲存節點x
到標點的最短路徑距離(或成本),使用陣列p[i]來儲存最
短路徑在第i階所經過的節點。

然後我們決定表格間不同元素的遞迴關係(如下頁所示)。

最後我們由底而上地(由第k, 第k-1,…,到第1階)填完表格
而得到解答。
58
動態規劃遞迴關係


動態規劃遞迴關係:
d[S, T] = min{1+d[A, T], 3+d[B, T],
6+d[C, T]}
59
動態規劃遞迴關係(續)

d[A, T] = min{2+d[D, T], 10+d[E, T]}
= min{2+(17+d[T,T]), 10+(12+d[T,T])}
= min{2+17+0, 10+12+0}=19.
邊界條件: d[T, T]=0
60
多階圖最小成本路徑演算法
Algorithm 多階圖最小成本路徑演算法
Input: 具n個節點的k階多階圖G(V, E), V= i=1..k Vi , ViVj= for ij,
V1={1},Vk={n}, <x,y>E (xVi  yVi+1), <x,y>的加權為w[x,y]
Output: 具k個節點由節點1到節點n的最小成本路徑(記錄在p[1..k]中)及其最
小成本(記錄在d[1]中)
1. d[n]=0; d[1..n-1]=; //陣列d[x]儲存節點x到標點t的最小距離(distance)
2. for ik-1 to 1 do
3.
for every node x in Vi 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; //陣列p[i]儲存最短路徑在第k階的節點
9. for i2 to k-1 do p[i]s[p[i-1]];
61
10. return p,d[1]
多階圖最小成本路徑演算法
時間複雜度






行1: 設定d[0],d[1..n]啟始值: O(n)
行2-4: 三層for迴圈實際執行|E|次迭代: O(|E|)
行5-7: 迴圈內if敘述執行需要常數時間
行8: 2個設定敘述
行9: for迴圈執行k-2次迭代: O(k)
行10: 回傳演算法執行結果需要常數時間
總時間複雜度: O(n + |E| + k)  O( |E| )
62
3.9
0/1背包演算法
63
定義 -- 0/1背包問題



給定一個最大載重容量(capacity)為m的背
包,以及n個可以放入背包的物品,其中第i
個物品的重量為wi>0,價格為pi>0
目標: 找出x1,…,xn以最大化
限制條件為  w i x i  m
p x
1i  n
i
i
1i  n
其中 xi=0 or xi=1, 1  i  n
(令 S={1,…,n}且T={i | xi=1}S)
64
使用暴力法解決0/1背包問題




0/1背包問題是一個最佳化問題(optimization
problem)。
我們可以測試S集合的所有2n個子集合來解這問題,
其時間複雜為O(2n)。
Q: 有任何比暴力法更好的演算法嗎?
A: 有。我們可以利用動態規劃(dynamic
programming)策略,用空間(表格或陣列)以換取
時間(trade space for time)。
65
0/1背包演算法
建構表格(陣列)



我們建構一個陣列P[0..n, 0..m]。
元素P[i, w], 1in, 0wm, 用以儲存物品集
合{1,2,…,i}的所有可能子集合中的物品最大總
價值,而這些物品的總重量小於等於w。
如果我們可以計算出陣列中的所有元素,那麼
元素P[n, m]儲存的值為物品集合{1,2,…,n}的
所有可能子集合中的物品最大總價值,而這些
物品的總重量小於等於m。
66
0/1背包演算法
確定遞迴關係

初始化設定(initialization):
P[0, w]=0 for 0wm

對所有的i與w(0in, 0wm)而言:
P[i, w]=
max(P[i-1, w], pi+P[i-1, w-wi]), if wiw
P[i, w]=P[i-1, w], otherwise
67
0/1背包演算法
由底而上的逐一將表格填完
 i, w由小而大,或稱由底而上(bottom up),根據
上頁的遞迴關係,逐一計算P[i, w]的值,將表格
填完。
P[i, w]
i=0
1
2
w=0
0
1
0
2
0
…
0
m
0
…
n
68
0/1背包演算法
Algorithm 0/1背包演算法
Input:背包的最大容量m,以及可能放入背包的n個物品(編號為1,…,n)的非
負重量wi與價格pi, 1in。
Output: 以全放或全不放的方式容納在背包中所有物品的最大總價值
1.
for w←0 to m do P[0, w]=0
2.
for i←1 to n do
3.
for w←0 to m do
4.
if wiw then
5.
P[i,w]=max(P[i-1, w], pi+P[i-1, w-wi])
6.
else
7.
P[i,w]=P[i-1, w]
8.
return P[n,m]
69
0/1背包演算法範例
Input: n=3, m=8
i
wi
pi
1
5
20
2
3
30
3
4
10
Table:
P[i,w] w=0
i=0
0
1
0
2
0
3
0
1
0
0
0
0
2
0
0
0
0
3
0
0
30
30
4
0
0
30
30
5
0
20
30
30
6
0
20
30
30
7
0
20
30
40
8
0
20
50
50
Output:
p[3,8]=50
70
0/1背包演算法
時間複雜度





行1: 設定P[0,1..m]初始值: O(m)
行2: 外層for迴圈執行n次迭代: O(n)
行3: 內層for迴圈執行m次迭代: O(m)
行4-7: 迴圈內敘述執行需要常數時間
行8: 回傳演算法執行結果需要常數時間
總時間複雜度: O(n  m)
71
0/1背包演算法時間複雜度討論
0/1背包演算法時間複雜度為O(nm)
 Q: 這是多項式時間嗎?
 A: 實際上這個演算法是屬於偽多項式時間(pseudopolynomial time)。
 如果一個演算法的時間複雜度是輸入數值(numeric value of
the input)的多項式,但卻是輸入長度(the length of the
input)的指數函數,那麼該演算法屬於偽多項式時間複雜度。
 例如,若m以長度為K位元的無正負號的二進位數字來表示,
則表格p有2k-1個直欄,而0/1背包演算法時間複雜度為
O(n2k),為輸入長度k的指數函數。
72
0/1背包演算法
如何找出哪些物品該放在背包中?


前面提到的0/1背包演算法只計算P[i,w],
也就是背包中物品的最高總價值,並沒有
記錄哪些物品所形成的子集合T(TS)可以
達成最高總價值的最佳解。
為了計算出實際達成最佳解的子集合,我
們增加一個輔助的布林陣列Q[1..n, 1..m]
。若P[i,w]得最佳解答時,第 i 個物品在
子集合T中,則Q[i,w]設為 1;否則Q[i,w]
設為 0。
73
0/1背包演算法
如何找出哪些物品該放在背包中??
 若Q[n, m]為1,則nT,我們可以遞迴地檢查Q[n-1, m-wn]
 若Q[n, m]為0,則 nT,我們可以遞迴地檢查Q[n-1, m]
 因此,以下的碼可以找出T中的元素。
k=m
T
for in to 1 do
if Q[i, k]=1 then
TT{i}
k=k-wi
 下頁中之完整演算法可以找出正確的集合T
與T所對應的物品最大總價值P[n,m]
74
Algorithm 0/1背包演算法
Input:背包的最大容量m,以及可能放入背包的n個物品的非負重量wi與價格pi
Output: T與p,其中T為物品子集,T中物品總重小於m而且可以產生最大總價值p
1.
for w←0 to m do P[0, w]=0
2.
for i←1 to n do
3.
for w←0 to m do
4.
if (wiw)  ((pi+P[i-1, w-wi])>P[i-1, w]) then
5.
P[i,w]= pi+P[i-1, w-wi]
6.
Q[i,w]=1
7.
else
8.
P[i,w]=P[i-1, w]
9.
Q[i, w]=0
10. k=m
更完整的0/1背包演算法
11.
T
12. for in to 1 do
13. if Q[i, k]=1 then
14.
T=T{i}
15.
k=k-wi
16. return T, P[n,m]
75
3.10
最長共同子序列演算法
76
最長共同子序列





以下我們說明最長共同子序列(Longest Common
Subsequence, LCS or LCSS)相關背景知識
令X為一個由若干符號依序排列組成的序列(sequence),
則X的子序列(subsequence)為從X刪除(不必要為連續性
的)0個或多個符號的序列
例: 令X = b a c a d,則ad, ac, bac, acad, bacad, bcd等
與空序列都是X的子序列
例: 序列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
77
最長共同子序列應用:
DNA序列比對
DNA = {A|C|G|T}*
(A: 腺嘌呤; C: 胞嘧啶; G: 鳥嘌呤; T: 胸腺嘧啶)
S1=ACCGGTCGAGTGCGGCCGAAGCCGGCCGAA
S2=GTCGTTCGGAATGCCGTTGCTGTAAA
Q: S1與S2是否為相似的DNA序列?
A: 這問題可以由找出S1與S2的最長共同子序列
來解決。
78
最長共同子序列應用:
化身路徑群組
化身路徑群組(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.
79
最長共同子序列應用:
化身路徑群組(續)

在第二人生(Second Life, SL)贈品島(Freebies
Island)的兩條路徑有多相似?
80
最長共同子序列應用:
化身路徑群組(續)
將化身路徑轉為序列:
將虛擬世界切割為方格
(grid cell),並針對化身
路徑每隔固定時間取樣,
找出其所在方格編號形成
序列。
SeqA:C60.C61.C62.C63.C55.C47.C39.C31.C32
81
最長共同子序列應用:
化身路徑群組(續)
SeqA :C60.C61.C62.C63.C55.C47.C39.C31.C32
SeqB :C60.C61.C62.C54.C62.C63.C64
LCSSAB :C60.C61.C62. C63
找出兩條路徑對應的
最長共同子序列以衡
量其相似程度。
82
最長共同子序列問題
以下我們定義最長共同子序列(Longest
Common Subsequence, LCS)問題:
 給定兩個序列X = <x1,x2,...,xm>, Y =
<y1,y2,...,yn>,找出X和Y的最長共同子序
列長度
83
最長共同子序列問題
暴力法時間複雜度
產生序列X(或Y)的所有子序列,然後檢查每
個子序列是否也是序列Y(或X)的子序列,然
後儲存下最長的子序列並輸出。複雜度為:
 n  2m = O(2m)
或
 m  2n = O(2n )
Q1: 如何產生一個序列的所有子序列?
Q2: 如何檢查一個序列是否為另一個序列的
84
子序列?
最長共同子序列動態規劃演算法
建構表格

我們定義Xi = < x1,x2,...,xi >
及Yj = <y1,y2,...,yj>。

我們建構表格c[0..m,0..n],令c [i, j]紀錄Xi 和Yj
的LCS的長度

則c[m, n]紀錄X = <x1,x2,...,xm>和Y =
<y1,y2,...,yn>的LCS的長度
85
最長共同子序列動態規劃演算法
遞迴關係
如前頁所定義,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 -1, j -1] +1
c[i, j] 
max{c[i, j -1], c[i -1, j]} if i,j>0 and x i y
j
j
86
最長共同子序列動態規劃演算法
由底而上的逐一將表格填完

i, j由小而大,或稱由底而上(bottom up),根
據上頁的遞迴關係,逐一計算c[i, j]的值,
將表格填完。
c[i, j]
i=0
1
2
…
m
j=0
0
0
0
1
0
2
0
…
0
n
0
0
0
87
最長共同子序列演算法
Algorithm 最長共同子序列演算法
Input: 兩個序列X = <x1,x2,...,xm>, Y = <y1,y2,...,yn>
Input: X和Y的最長共同子序列長度
1. m  |X| //m記錄序列X的長度
2. n  |Y|
//n記錄序列Y的長度
3. for i  1 to m do c[i, 0]  0
4. for j  1 to n do c[0, j]  0
本演算法參考以下書籍改寫:
CORMEN, Thomas H., et al. Introduction to algorithms. Cambridge: MIT press, 2001.
88
最長共同子序列演算法(續)
我們同時建構陣列
5. for i  1 to m do
d[1..m, 1..n],並使
6.
for j  1 to n do
用d[i,j]記錄c[i,j]最
7.
if xi = yj then
大值的參考由來
8.
c[i, j]  c[i-1, j-1]+1
9.
d[i, j]  “” //對應i-1及 j-1
10.
else if c[i–1, j]  c[i, j-1] then
11.
c[i, j]  c[i-1, j]
12.
d[i, j]  “” //對應i-1
13.
else
14.
c[i, j]  c[i, j-1]
15.
d[i, j]  “” //對應 j-1
16. return c and d
89
最長共同子序列演算法
時間複雜度
 行5的外層for迴圈一共有m次迭代
 行6的內層for迴圈一共有n次迭代
 行7-15的if敘述需要常數時間
因此總時間複雜度為O(mn) ,相對於暴力法的
O(2m)或 O(2n),有非常大的改善。
90
最長共同子序列演算法
執行範例
X=DBCBE
Y=BACDBA
91
最長共同子序列演算法
如何找出最長共同子序列
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
LCS //LCS初始值為空序列
im; jn
while |LCS| <c[m, n] do // |LCS| 表示LCS的長度
if d[i, j] = “” then
LCS xi || LCS
// || 表示序列連結
i--; j--;
行18-25while迴圈時間複雜
else if d[i, j] = “” then
度為O(m+n),因為當i由m
i-減為1時且/或j由n減為1時
else // d[i, j] = “”
一定會產生正確的LCS
j-return LCS, |LCS| (也就是c[m, n])
92
3.11
矩陣鏈乘積演算法
93
二個矩陣相乘所需的純量乘法數



假設 M 是一個xy的矩陣,N 是一個yz的矩
陣, 則 MN需要xyz次的純量乘法計算。
a d
1 2 3
例如: M = 4 5 6 2x3 , N = b e
c f 3x2
1a + 2b + 3c
則MN =
4a + 5b + 6c
1d + 2e + 3f
4d + 5e + 6f
2x2
94
矩陣相乘執行順序非常關鍵



假設M1是個5200的矩陣,M2是一個20010
的矩陣,M3是一個10100的矩陣。
那麼算出((M1M2)M3)需要
520010+1010100=5000+10000=
15000次的純量乘法計算。
然而算出(M1(M2M3))卻需要20010100 +
5200100=200000+100000=
300000次的純量乘法計算。
95
矩陣鏈乘積問題
定義:
矩陣鏈乘積問題(matrix-chain product problem)

給定一個含n個矩陣的矩陣鏈乘積
M1M2…Mn,其中矩陣Mi, 1in, 的維度為
ei-1ei 。 找 出 一 種 M1M2…Mn 的 完 全 括 號
(fully parenthesized)方式,以便可以使用最
少的純量乘法求出M1M2…Mn的乘積。
96
完全括號矩陣鏈乘積



若 一 個 矩 陣 鏈 乘 積 為 完 全 括 號 (fully
parenthesized),則其為單一矩陣或為兩個完
全括號矩陣鏈乘積相乘並加括號。
例如: X、Y是完全括號矩陣鏈乘積,(XY)也
是完全括號矩陣鏈乘積
又例如: ((XY)Z)與(X(YZ))都是完全括號矩
陣鏈乘積
97
矩陣鏈乘積問題
窮舉所有不同的完全括號方式
長度為n的矩陣鏈乘積不同的完全括號方式的總數P(n):
 相當於將矩陣鏈乘積在第k個矩陣之後與第k+1個矩陣
之前,使用括號分為二組後再計算其乘積,而1kn-1
。我們可得:
1
if n  1


P(n)   n -1
P(k)P(n - k) if n  2

 k 1

實際上,P(n)為對應到n-1的卡塔蘭數(Catalan
number)=Cn-1 =O(2n)
98
卡塔蘭數(Catalan number)


卡塔蘭數是以比利時的數學家卡塔蘭(Eugène
Charles Catalan, 1814–1894)命名。
卡塔蘭數的一般項公式為
n
2n


1
4
Cn  n + 1  n   O( n 3/2 )
99
矩陣鏈乘積演算法
建構表格



使用表格m[1..n, 1..n],定義 m[i, j] = 計算
Mi…Mj所需的最小純量相乘數,其中Mi
的維度為ei-1  ei,1 i, j n。
目標為求得m[1, n]
除了表格m之外,矩陣鏈乘積演算法利用
另一個輔助表格s[1..n, 1..n]來記錄矩陣鏈
分割(split)的位置。s[i, j]記錄哪一個分割
位置造就了m[i, j]的最小純量相乘數。
100
矩陣鏈乘積演算法
遞迴關係


定義 m[i, j]= 計算Mi…Mj所需的最小相乘數
,其中Mi維度為ei-1ei,1 i, j n。
0
i j

m[i, j]= 
minik j{m[i, k] + m[k + 1, j] + ei-1e k e j} i  j
101
矩陣鏈乘積演算法
由底而上的逐一將表格填完

因為邊界條件為i=j,因此我們根據11,22,…,nn,
12,23,…,(n-1)n, 13,24,(n-2)n, …, 1n的方式填表,也就是
根據 j-i的差值d由0到n-1,或稱由底而上(bottom up),根
據上頁的遞迴關係,逐一計算m[i, j]的值,將表格填完。
d=n-1
d=…
因為ji,因此
表格中有一半
的格子不使用。
d=2
d=1
d=0(符合邊界條件的初始值)102
矩陣鏈乘積演算法
本演算法參考以下書籍改寫:
CORMEN, Thomas H., et al. Introduction to algorithms.
Cambridge: MIT press, 2001.
Algorithm 矩陣鏈乘積演算法
Input: 矩陣鏈乘積M1M2…Mn
Output: 計算矩陣鏈乘積M1M2…Mn所需最少的純量乘法數
1. for i  1 to n do
2.
m[i, i]  0
3. for d  1 to n – 1 do
4.
for i  1 to n – d do
5.
ji+d
6.
m[i, j]  
7.
for k  i to j – 1 do
8.
t  m[i, k] + m[k+1, j]+ ei-1ekej
9.
if t < m[i, j] then
10.
m[i, j]  t
11.
s[i, j]  k
12. return m and s
103
矩陣鏈乘積演算法
時間複雜度
 行3的外層for迴圈一共有O(n)次迭代
 行4的中層for迴圈一共有O(n)次迭代
 行7的內層for迴圈一共有O(n)次迭代
 行8-11的敘述需要常數時間計算
因此總時間複雜度為O(n3)
104
矩陣鏈乘積演算法
印出最佳全完括號矩陣鏈乘積
Algorithm 印出完全括號矩陣鏈乘積(s, i, j)
Input: 矩陣鏈乘積演算法輸出的表格s,整數i與j, ij
Output: 將完全括號矩陣鏈乘積印出
藉由呼叫印出完全括號矩陣鏈乘積(s, 1, n)就可
1. if i=j then
以印出M M …M 的完全括號方式。因為陣列s
2.
print “M” ||i 是矩陣鏈乘積演算法產生的,用以記錄產生最
少純量乘法數的完全括號矩陣鏈乘積,因此呼
3. else
叫印出完全括號矩陣鏈乘積(s, 1, n)印出的是
M M …M 的最佳完全括號方式。
4.
print “(“
5.
印出完全括號矩陣鏈(s, i, s[i,j])
6.
印出完全括號矩陣鏈(s, s[i,j]+1, j)
本演算法參考以下書籍改寫:
7.
print “)”
1
1
2
2
n
n
CORMEN, Thomas H., et al. Introduction to algorithms.
Cambridge: MIT press, 2001.
105
3.12
Bellman-Ford
最短路徑演算法
106
Bellman-Ford最短路徑演算法
介紹
與Dijkstra演算法相同,Bellman-Ford演算
法也是屬於求取單一源點(single source)至
全部標點(all destination)的一至全(one-toall)最短路徑演算法。
但是與Dijkstra演算法不同的是,BellmanFord演算法可以在具有負加權邊(negativeweight edge)的圖也可以正確的執行,並且
可 以 檢 查 圖 是 否 有 負 加 權 循 環 (negativeweight cycle)。

107
Bellman-Ford最短路徑演算法
介紹(續)
Bellman-Ford最短路徑演算法採用動態規劃
策略解決問題,找出由源點s到其他全部節點
的最短路徑。
 Bellman-Ford演算法一開始在第1次迭代先求
出所有屬於1-邊路徑(1-edge path)的最短路徑
,並將其最短路徑距離儲存在陣列中。
 所謂1-邊路徑,就是由源點s開始,只能經過
1個邊所造成的路徑。
 所謂k-邊路徑,就是由源點s開始,只能經過
k個邊所造成的路徑。

108
Bellman-Ford最短路徑演算法
介紹(續)
然後Bellman-Ford演算法基於儲存在陣列中
的1-邊最短路徑結果,在第2次迭代針對每個
邊(u, x),由u往外調整到x的最短路徑距離,
可以得出所有屬於2-邊路徑(2-edge path)的最
短路徑。
 依此類推則在第n-1次迭代可以求出所有屬於
(n-1)-邊路徑((n-1)-edge path)的最短路徑。
 因為具n個節點的圖的最長路徑最多具有n-1
個邊,因此第n-1次迭代求出的路徑已經是最
終的正確結果了。

109
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],及每個節
點u最短路徑上的前節點(predecessor)p[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.
p[x]← u
7. for 每一個G的邊(u, x) do //檢查有無負循環(negative-weight cycle)
8.
if d[x] > d[u] + w[u][x] then exit //代表有負循環,無法產生正確結果
9. return d, p
110
Bellman-Ford最短路徑演算法
時間複雜度
假設G一共有n個節點,m個邊(也就是|V|=n, |E|=m)
 行2-6的外層for迴圈一共有O(n)次迭代
 行3-6的內層for迴圈一共有O(m)次迭代
 行4-6為內層if指令,針對每個邊(u, x)依據目前的d[u]值調
整d[x],其執行需要常數時間
 行7-8的for迴圈在求出(n-1)邊路徑之後再針對每個邊(u, x)
,檢查目前的d[u]值,一共有O(m)次迭代
因此總時間複雜度為O(n  m)= O(|V|  |E|)
111
Bellman-Ford最短路徑演算法
執行範例
112
3.13
Floyd-Warshall
最短路徑演算法
113
Floyd-Warshall最短路徑演算法
介紹

與Dijkstra演算法與Bellman-Ford演算法不同的是,
Floyd-Warshall演算法可以求出全部節點配對的最
短路徑,是一個全配對最短路徑(all-pair shortest
path)演算法。

Floyd-Warshall演算法可以處理具有負加權邊的圖
,但是不能用以檢查一個圖是否具有負加權循環。
114
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)E;
w[i][j]=the weight of (i, j), for (i, j) E)
Floyd-Warshall演算法執行時會不斷的更新陣列d。在第
k次更新陣列d時,表示d中所紀錄的最短路徑是經由編
號小於或等於k的節點當作中間節點所造成的。因此,
當第n次更新陣列d時,則表示d中所紀錄的最短路徑是
可以經由所有節點當作中間節點所造成的,這也就是
演算法所需要的結果。
115
Floyd-Warshall最短路徑演算法
Algorithm Floyd-Warshall最短路徑演算法
Input:一個加權有向圖(weighted digraph)G,其中節點編號為1,…,n,而
各邊的加權值以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 //針對每個編號由1至n的節點
4.
for j←1 to n do //針對每個編號由1至n的節點
5.
if d[i][j] > d[i][k]+d[k][j] then
6.
d[i][j]←d[i][k]+d[k][j]
7.
p[i][j]←p[k][j]
8. return d, p
116
Floyd-Warshall演算法討論

前節點(predecessor)陣列p紀錄每個節點
在最短路徑上的前節點。在進行p[i][j]初
始化時,若i=j或(i,j)∉E則p[i][j]初始化為
NIL(),否則(也就(i,j)E)p[i][j]初始化
為i。

演算法執行完畢之後,則可藉由前節點陣
列p來找出由任意節點到其他任意節點最
短路徑上經過的所有節點。
117
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]
118
Floyd-Warshall最短路徑演算法
時間複雜度
假設G一共有n個節點(也就是|V|=n)
 行2的外層for迴圈一共有n次迭代
 行3的中層for迴圈一共有n次迭代
 行4的內層for迴圈一共有n次迭代
 行5-7迴圈內if敘述的執行為常數時間
因此總時間複雜度為O(n3)=O(|V|3)
119
The End
120
Download