COMP9101 算法設計 week4

貪心算法

問題一:
一個活動ai的列表,1<=i<=n,si時間開始,fi結束,沒有兩個活動在同時發生。
任務:找到最大兼容size的子集。

嘗試1:總是選擇最短且與之前不衝突活動,刪掉衝突的活動?
在這裏插入圖片描述
不可以。

嘗試2:選擇一個與其他衝突最小的activity。看起來對未來選擇限制最小。
在這裏插入圖片描述
不可以。

**正確的策略:**在不與之前選擇的衝突下,總是選擇結束時間最早的。
在這裏插入圖片描述

證明這個貪心算法的最優

證明任何最優解與貪婪算法的活動數量相同:

  1. 找到違反貪婪原則的第一個的活動。
  2. 表明用貪婪算法的選擇代替這個活動可以得到相同數量的活動。
  3. 循環以上步驟知道貪婪算法完全代替最優解。
    在這裏插入圖片描述
    時間複雜度:
    我們將他們的對初始時間和結束時間排序,按結束時間的增序。
    —>時間複雜度:O(nlogn)
    我們按順序查看列表,找到第一個開始時間是在前一個活動的結束時間之後的活動。
    每個活動被執行一次,所以每次需要O(n).
    所以,總的時間複雜度是O(nlogn).

問題二:
一個活動列表同問題一,但結束時間是fi=si+d. 所以所有的活動的運行時間都相同。沒有兩個活動佔用同樣的時間。

任務:找到一個有最大持續時間的活動子集。
因爲所有的活動持續時間一樣,等價於找到不衝突的活動的最大個數。

如果持續時間不一樣呢?
貪心策略不能用了。—>動態規劃

問題三:
Telstra想爲住戶提供網絡服務,基站範圍是5km。放置最少的基站覆蓋所有房屋。

貪心算法:
在這裏插入圖片描述
一名Telstra工程師從最靠近左邊的房屋開始,並在5公里處放置了一座塔。然後,他找到了不在基站範圍內的最西側的房屋,並在距基站以東5公里處放置了另一座塔樓,並以此方式循環。
他的另一名同事做了相同的事但是從右向左,並稱用了更少的塔?
答案是否定的。因爲最優算法左右都是一樣的。

問題四:
初始時間T0,有一系列工作ai,1<=i<=n, 持續時間ti並且截止時間di。同事間只有一個工作在進行。所有工作必須完成。如果一個工作ai在fi>di的時間完成,則算推遲時間li=fi-di。

任務:計劃所有的時間,使job最大的推遲時間最短。
解決方式:忽略工作持續時間並且按截止時間的升序安排工作。
最優解:考慮最優解不包含反轉。如果ai被安排在 aj之前但dj <di,則作業ai和aj形成一個反轉。
在這裏插入圖片描述
如圖,dj<di, 但工作i在工作j之前被安排。

我們需證明存在一個解沒有任何的相反。
冒泡排序消除反轉!
在這裏插入圖片描述
因爲反轉後fi-di和f(i+1)-d(i+1)都小於反轉前的f(i+1)-d(i+1),
交換相鄰的相反的工作減少最大延遲時間。所以交換直到消除反轉。

問題5:Tape storage
一個有着n個files,長度爲li的列表需要存儲到磁帶上。每個文件可能性均等。要檢索一個文件,必須從頭開始掃描直到找到磁盤上對應文件。
任務:對磁盤上文件排序使平均檢索時間最小。
解決方法:如果文件按l1…ln的順序存儲,所以預計時間是:
l1+(l1+l2)+(l1+l2+l3)+…+(l1+l2+l3+…+ln)=nl1+(n-1)l2+(n-3)l3+…+2l(n-1)+ln
如果按照l1<=l2<=…ln排列則檢索時間這是最小的。

問題6: Tape storage2
一個有n個文件fi,長度是li並且每個文件被需要的概率是pi,Sum(pi)=1,需要存進磁盤。要檢索一個文件,必須從磁盤頭開始掃描直到找到磁盤上對應文件。
任務:對磁盤上文件排序使平均檢索時間最小。
解決方法:如果文件按l1…ln的長度遞增順序存儲,需要時間:
p1l1+(l1+l2)p2+(l1+l2+l3)p3+…+(l1+…+ln)pn.
所以,當文件按照pi/li的值的遞減順序排序存儲,則時間最小。

如果我們將fk與fk+1長度互換會怎樣?

在這裏插入圖片描述
問題7:
X爲實線上n個區間的集合。如果X中的每個區間都包含P中的至少一個點,則一組P點會刺入X。找到高效算法刺入X的最小點集。假設輸入包括了2個數組:XL[1…n]和XR[1…n],用於表示X區間的左右端點。
在這裏插入圖片描述
策略:找到第一個右端點插入刺,然後循環。

0-1 揹包(knapsack)問題

一個有離散物品ai的重量wi和值vi的列表,1<=i<=n, 並且揹包有最大重量。
任務:找到一個所有物品的子集S,使重量不超過W,並且值是最大。

我們可以每次都選每重量單位裏值最大的物品放入揹包嘛?
不可以。
在這裏插入圖片描述
沒有簡單的方法說什麼時候用貪心算法。

問題8:
假設有n個不同size的數組。允許任意兩個數組merge直到只剩下一個數組。設計一個算法實現它,並使元素移動個數最小。

解法:首先對數組從小到大排序。每次merge長度最小的兩個array。

霍夫曼編碼

一種方式是給定需要編碼的不同符號的數目,保存成長度相等且足夠長的位字符串;假設有26個字母加上6個標點符號,則需要長度爲5的字符串(2^5=32).
但這並不划算:所以的符號有相同長度的編碼,但他們出現的頻率並不相同。
所以對於元音這種頻率高的使用短的編碼,頻率低的使用長編碼。
但對變長的字節流,如何讓唯一地劃分成與代碼相對應的段?
確保編碼的唯一性的一種方法是確保一個符號的任何編碼都不是另一個符號的編碼前綴。—>前綴編碼。

給定每個符號的頻率(出現的概率),設計最佳前綴碼,即前綴碼,使編碼文本的長度儘可能小。
這等於平均長度的文本中每個符號的平均位數儘可能小。

在這裏插入圖片描述
首先對符號出現頻率排序,先merge概率小的:
在這裏插入圖片描述

貪心算法的圖應用

問題9:
有n個廣播海嘯警報的無線電塔。每個塔給定(x,y)座標及其範圍半徑。激活塔後,位於塔範圍內的所有塔也將激活,而那些塔可能導致其他塔也激活,依此類推。
我們需要爲其中一些塔配備地震傳感器,以便當這些傳感器激活這些傳感器所在的塔時,所有塔最終都會被激活併發出海嘯警告。
目標是設計一種算法,以找到必須配備地震傳感器的塔數最少的算法。

有人提出了以下兩種算法:
算法1:找到半徑最大的未激活塔(如果多個半徑相同,則選擇其中任何一個)。激活此塔。查找並刪除所有激活的塔。重複下去。
算法2:查找在其範圍內具有最多塔數的未激活塔。如果沒有,激活最左邊的塔。重複下去。

給定一個有向圖G =(V,E)和一個頂點v,包含v的G的強連通分量包括所有頂點u∈V,使得G中存在從v到u的路徑以及從u到v的路徑。

我們如何找到包含u的強連通分量C⊆V?

構造另一個圖Grev =(V,Erev),該圖由相同的頂點集V組成,但通過反轉G的所有邊緣E的方向而獲得的一組邊緣Erev。

使用BFS查找V中所有頂點的集合D⊆V,D能夠從v到G。
同樣BFS找到點集R⊆V,D能夠從v到Grev。

包含v的G的強連通分量C是集合C = D∩R。

顯然,不同的強連通分量是不相交的集合。

令SG爲圖G的所有強連通分量的集合。

我們定義了一個圖Σ=(SG,E ∗),其中頂點集SG和強連通分量之間的有向邊E ∗,使得如果σ1∈SG和σ2∈SG且σ1!=σ2, 則在E ∗中存在從σ1到σ2的邊,只要存在一個頂點u1∈σ1和一個頂點u2∈σ2從而在E中存在從u1到u2的邊緣。
在這裏插入圖片描述
顯然,Σ=(SG,E ∗)是有向無環圖,因此可以對頂點進行拓撲排序。
在這裏插入圖片描述
有向無環圖的拓撲排序是其頂點σi∈SG的線性排序(枚舉),因此,如果存在邊(σi,σj)∈E ∗,則頂點σi以這樣的順序排在σj之前,即i <j 。
在這裏插入圖片描述
我們如何對有向無環圖進行拓撲排序?
在這裏插入圖片描述
用以上算法解決了塔廣播問題。

優先隊列

我們需要一個優先級隊列,它允許有效地更改隊列中元素的鍵,因此我們首先需要擴展堆的數據結構。
我們將使用數組表示的堆;A[i]的左孩子存儲在A[2i]中,右孩子存儲在A[2i+1]中。
我們將以各種方式計算鍵,將圖的頂點存儲在堆中;如果圖有n個頂點,我們將用1到n的正整數標記它們。
因此,A的每個元素都是(i,k(i))形式,其中k(i)是元素i的鍵。
除了表示堆的數組A之外,我們還將使用另一個長度相同的數組P來存儲元素在堆中的位置;因此A[P[i]]=(i,k(i))。
更改元素i的鍵是O(log n)的操作:我們在A中查找它的位置P[i],在A[P[i]]中更改元素的鍵,然後執行heapify操作以確保保留堆的屬性。

1959的迪傑斯特拉算法

假設我們得到了一個有向圖G=(V,E),每個邊e∈E被賦予權重w(e)≥0。
我們還有了一個頂點v∈V。
爲了簡化,我們假設對於每個u∈V都有一條路從v到u。
我們的任務是找到對於每個u∈V,從v到u的最短路徑。
在這裏插入圖片描述

該算法將建立一組已經建立最短路徑的頂點,從單個源頂點S = {v}開始,然後一次添加一個頂點。

在構造的每個階段,我們添加元素u∈V \ S,該元素具有從v到u的最短路徑,並且所有中間頂點都已經在S中。
在這裏插入圖片描述
爲什麼這會產生從v到g的最短路徑?
假設相反,G中存在從v到u的更短的短路徑。根據我們的選擇,這種路徑不能完全在S中。
令z爲S之外的第一個頂點(如果它剛好在添加u之前)在最短的路徑上.
在這裏插入圖片描述

由於沒有負的權重邊,因此從v到z的路徑比從v到u的路徑短,這與我們對u的選擇相矛盾。

如何有效地構建優先級隊列?
最初,除v以外的所有頂點都被放置在具有額外的Position數組的基於堆的優先級隊列中,如果(v,u)∈E,則權重爲w(v,u);否則鍵爲∞。

隨着我們的進度,每個元素u的鍵將更新爲長度爲lh(S,v)(u),即從v到u的最短路徑的長度v(u),該路徑在S中具有所有中間頂點。

我們總是從優先級隊列中彈出最小的key的元素u,並將其添加到集合S中。
然後我們尋找所有元素z(屬於V\S 且(u,z)∈E),如果lh(S,v)(u)+ w(u,z)<lh(S,v)(z),我們將z的key更新爲值lh(S,v)(u)+ w(u,z)。

爲什麼只有更改後的最短路徑有u作爲S的最後一個頂點?

在這裏插入圖片描述

假設相反,當u被添加時到頂點x的最短路徑已更改,y代替了u,成爲了x之前的最後一個頂點。
但是,這是不可能的,因爲在添加u之前,它會生成到y的最短路徑,該路徑上的頂點u不屬於集合S。
如果圖G有n個頂點和m個邊,那麼每一條邊只檢查一次,當它是最近添加的頂點的邊時;更新一個頂點鍵需要O(logn)步。
所以,總共需要時間O(mlogn)。

最小生成樹

定義:連通圖G的最小生成樹T是G的一個子圖(具有相同的頂點集),它是一棵樹,在所有這些樹中,T的所有邊的總長度最小。

理論:設G是一個連通圖,G的所有邊E都是獨立的,S是G的所有頂點V的集合的非空的真子集。假設E=(u,V)是一個邊,使得u∈S和v(不屬於S)在所有具有此性質的邊中是最小長度的。那麼e必須屬於G的每個最小生成樹T。

證明:
在這裏插入圖片描述
在這裏插入圖片描述

克魯斯卡爾Kruskal算法

我們對邊E的權重按非遞減順序排序。

我們通過添加邊來構建樹,每一步都要添加一個邊。

當一個邊ei添加到現有的圖中沒有形成一個環時,它就被在此輪(i)添加。

如果添加邊確實形成了環,則丟棄該邊。

當所有邊被添加完時,進程終止。

聲明:Kruskal算法生成一個最小生成樹,如果所有的權重都是不同的,那麼這樣的最小生成樹是唯一的。
證明:
在這裏插入圖片描述

聯合-查找數據結構用於實現Kruskal

在這裏插入圖片描述注意,我們不給出單個並集操作的運行時間,而是給出k個連續此類操作的初始序列的運行時間。

這種時間複雜度分析稱爲攤銷分析;它(基本上)估計一系列操作的平均成本,在這種情況下爲log k。
我們將使用其元素之一標記每個集合。由於我們將使用聯合-查找數據結構來跟蹤圖的頂點集,因此我們可以假設基礎集S的形式爲S = {1,2,…,n}。

簡單的聯合-查找數據結構包括:
1.數組A :A [i] = j表示i屬於標記爲j的集合;
2.數組B,使B [i]包含集合i中元素的數量(可以爲0)和指向i鏈表第一個和最後一個元素的指針。

Union(i,j):
1.如果由i標記的集合中的元素數大於或等於由j標記的集合中的元素數,則將由j標記的集合中所有元素的數組A中的標記更改爲i,並相應地更新數組B;
2.如果用j標記的集合的元素多於用i標記的集合的元素,則將用i標記的集合中所有元素的標籤更改爲j,並相應地更新數組B。

請注意,該定義暗示着,如果Union(i,j)更改了包含元素m的集合的標籤,則包含m的新集合將至少具有在Union(i,j)之前包含m的集合的元素數目的兩倍。

任何k個初始連續Union操作最多可以接觸S的2k個元素(如果將所有Union操作都應用於單個集合,則會發生這種情況)。

因此,在k個初始連續並集之後包含元素m的集合必須具有少於2k個元素。

因爲每次更改包含m的集合的標籤的Union操作至少會使包含該元素的集合的大小增加一倍,所以包含m的集合的標籤變化可能小於log 2k多次。

因此,由於我們最多有2k個元素,因此,任何k個初始連續Union操作的總數都小於(2k log 2k)個 A中的標籤更改,每個Union操作僅更改B中的幾個指針,並增加了集合的大小。

因此,k個初始連續聯合操作的每個序列的時間複雜度爲O(k logk)。

聯合-查找數據結構足夠好讓Kruskal算法的運行時得到最sharpest的可能bound。

基於指針和路徑壓縮的聯合-查找數據結構,進一步降低了Union操作的攤餘時間複雜度,代價是將Find操作的時間複雜度從O(1)增加到O(log n)。

現在,我們使用先前描述的聯合-查找數據結構在具有n個頂點和m個邊的圖G =(V,E)上有效地實現Kruskal算法。

我們首先必須對圖G的m個邊進行排序,這需要花費時間O(m log m)。由於m≤n2,該算法的這一步也需要
O(m log n2)= O(m log n)。

隨着Kruskal算法進行,我們將構建連通分量,這些分量將合併爲更大的連通分量,直到所有頂點都屬於單個連通分量。爲此,我們使用聯合-查找數據結構來跟蹤到目前爲止構造的連通分量。

對於邊排序列表上的每個邊e =(v,u),我們使用兩個Find操作,即Find(u)和Find(v)來確定u和v的頂點是否屬於同一分量。

如果它們不屬於同一分量,即,如果Find(u)= i和Find(v)= j,j!= i,則將邊e =(u,v)添加到正在構造的生成樹中並執行聯合(i,j)將u和v放入同一連接的分量中。

我們總共執行2m次查找操作,每個操作的成本爲O(1),總成本爲O(m)。

我們還執行n-1個Union操作,其總成本爲O(n log n)。

邊的初始排序需要O(m log m)= O(m log n2)= O(m log n)個操作。因此,我們獲得了總體時間複雜度爲O(m log n)。

k-clustering of maximum spacing

實例:一個完整的圖G,其加權邊表示兩個頂點之間的距離。

任務:將G的頂點劃分爲k個不相交的子集,使得屬於不同劃分集的兩個點之間的最小距離儘可能大。因此,我們想要一個劃分成k個儘可能遠的不相交集。
在這裏插入圖片描述
解決方案:按遞增順序對邊進行排序,並開始執行通常的Kruskal算法來構建最小生成樹,但在獲得k個連接分量而不是單個生成樹時停止。

證明是最優解:設d是與未添加到k連通分量的最小生成樹的第一邊相關聯的距離;顯然是屬於兩個k連通分量之間的最小距離。顯然,由我們的算法產生的k個連通分量中包含的所有邊的長度都小於或等於d。

考慮將任何分區S分成k個子集,這些子集與我們的算法產生的子集不同。
在這裏插入圖片描述
這意味着我們的算法產生了一個連通分量,其中包含頂點vi和vm,使得對於某些Sj∈S,vi∈Sj。但vm 不∈Sj。
在這裏插入圖片描述
在這裏插入圖片描述
由於vi和vm屬於相同的連通分量,因此該分量 中存在一條連接vi和vm的路徑。

令vp和vp + 1是該路徑上的兩個相鄰的頂點,使得vp屬於Sj,vp +1不∈Sj。

因此,對於某些n!= j,vp +1∈Sn。

請注意,d(vp,vp + 1)≤d意味着這兩個聚類Sj,Sn∈S之間的距離小於或等於我們算法生成的k個連接組件之間的最小距離d。

因此,與我們的算法所產生的分區相比,這種分區不可能是最佳的聚類。

該算法的時間複雜度:
我們有O(n2)條邊;因此,按權重對它們進行排序將需要
O(n^ 2 log n2)= O(n^ 2 log n)。

在運行(部分)Kruskal算法時,我們使用聯合查找
需要O(n ^2 log n)個步驟的數據結構。

因此,整個算法的總時間爲O(n ^2 log n)。

REFERENCE

Comp9101 week4 lecture recording

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章