謹(jǐn)以此文,感謝我在這個學(xué)校最喜歡的兩個老師之一——肖my老師。本文基本為老師上課說講授內(nèi)容加上一部分自己的感悟拼湊而來,寫作文本的目的是為自己的算法課程留下一點點東西,站在老師肩膀上形成粗糙的框架,方便以后的復(fù)習(xí)以及深入。文筆有限,其中包含的錯誤還請多多包容,不吝賜教。
to do list:
時間復(fù)雜度中遞歸樹法;動規(guī),分治新的感悟;
一:基礎(chǔ)
部分算法、概念的前置知識
點覆蓋:一組點的集合,使得圖中所有邊都至少與該集合中一個點相連。
支配集:一組點的集合,使得圖中所有的點要么屬于該集合,要么與該集合相連。
最大團(tuán):在一個無向圖中找出點數(shù)最多的完全圖。
獨立集:一組點的集合,集合中的頂點兩兩不相鄰。(團(tuán)轉(zhuǎn)過來)
SAT問題:也稱布爾可滿足性問題。給一組變
其中Ci被稱為句子。
點覆蓋<->獨立集<->最大團(tuán)
最小割:割是一組邊集。如s-t割就是如果去掉這些邊,將把原圖劃分為兩個點集,其中一個點集包含s,一個點集包含t。(兩個是指不相連,而不是代表不存在邊相連,如反向邊)
decision problem: 是否存在。
search problem:找到一個解。
(這個還能擴(kuò)展,比如decision problem在多項式時間內(nèi)解決,所以他是P問題嗎)
漸進(jìn)符號:
注意以上三種都是緊的,對應(yīng)的兩個小寫的符號是不緊的,即如下圖所示:
時間復(fù)雜度
概念:算法的時間復(fù)雜度是一個函數(shù),用于定性描述算法的運(yùn)行時間。注意,這個一個代表算法輸入字符串長度的函數(shù)。
[注]輸入字符串長度是一個比較關(guān)鍵的理解,比如在背包問題中,其時間復(fù)雜度為O(nW),因為W不定,所以只能是一個偽多項式時間。
比較:c < log2N < n < n * Log2N < n^2 < n^3 < 2^n < 3^n < n! < n^n
大致:常數(shù)<對數(shù)<冪函數(shù)<指數(shù)函數(shù)<階乘
對于指數(shù)是n相關(guān)的進(jìn)行比較,優(yōu)先比較指數(shù),再比較底數(shù)。
記住一個特例:n(logn)<n!<nn
計算:
一般來說,計算采用主方法和遞歸樹法,其中遞歸樹技巧性比較強(qiáng),主方法其實也是遞歸樹推導(dǎo)歸納而來,且主方法能得到一個比較緊的結(jié)果。
主方法:
f(n) = af(n-b)+g(n) =>O( a^(n/b) *g(n) )
P,NP,NP-Complete和NP-Hard
P:decision problems有一個多項式算法。
NP(nondeterministic polynomial-time):decision problems能夠在多項式時間內(nèi)驗證。
NPC:NP完全問題,首先這個問題是NP的,其次,其他所有問題都可以多項式時間內(nèi)歸約到它。
NPH:如果所有NP問題都可以多項式時間歸約到某個問題,則稱該問題為NP困難。
因為NP困難問題未必可以在多項式時間內(nèi)驗證一個解的正確性(即不一定是NP問題),因此即使NP完全問題有多項式時間的解(P=NP),NP困難問題依然可能沒有多項式時間的解。因此NP困難問題“至少與NP完全問題一樣難”。
一些NP問題能在多項式時間內(nèi)解決,因為 P∈NP
NP難類型問題的證明:
先選好一個已知NP難的問題,然后將已知NP難問題多項式歸約到要證明的問題上。先給出這個歸約,然后再證明這個歸約的正確性。
NPC類型問題的證明:
證明一個問題Y是NPC問題,先說明Y是NP的,然后找到一個NPC問題X,將這個問題X歸約到問題Y上,即證明完成。
常見的NPC問題(重要,規(guī)約的時候有用!):
packing problems: set-packing,獨立集
覆蓋問題:集合覆蓋問題,頂點覆蓋問題
嚴(yán)格滿足問題(constraint satisfaction problems):SAT,3SAT
序列問題:哈密爾頓回路,旅行商問題
劃分問題:3D-matching, 3著色問題
數(shù)字問題:子集合問題(子集元素之和為t),背包問題
其他:分團(tuán)問題(是否存在一個規(guī)模為k的團(tuán))
規(guī)約的概念與理解
規(guī)約:意味著對問題進(jìn)行轉(zhuǎn)換,例如將一個未知的問題轉(zhuǎn)換成我們能夠解決的問題,轉(zhuǎn)換的過程可能涉及到對問題的輸入輸出的轉(zhuǎn)換。
自歸約:search problem <=p decision problem
歸約:A歸約到B,也就是說,我們對A套一個函數(shù)f,在f函數(shù)的作用下形成一個新的問題,對這個問題運(yùn)用B的黑盒解法,能夠解決問題A。
(B <=p A)一般說來,B問題如果可以歸約到A問題,也就是說,一個解決A問題的算法可以被用做子函數(shù)(子程序)來解決B問題,也就是說,求解B問題不會比求解A問題更困難。因此,如果B問題是困難的,那么A問題也就是困難的,因為不存在求解A問題的高效算法。(最后一句不懂)
我簡單說一下我理解的規(guī)約,以X規(guī)約到Y(jié)為準(zhǔn),大概分成兩個方面:
X規(guī)約到Y(jié),是說X問題能夠通過函數(shù) f 轉(zhuǎn)化成Y,即如果Y是一個黑盒解法,那么我對X的每一個實例通過 f 轉(zhuǎn)化成Y之后,都能用這個黑盒解出來,然后再把Y的答案轉(zhuǎn)回X的答案,一般來說,這個轉(zhuǎn)回在定義 f 的時候就有說明。
一般來說,做這個規(guī)約是為了解決問題X,也就是說,在具體規(guī)約的時候,①應(yīng)該對X的實例進(jìn)行刻畫,肉眼找到答案,②然后對這個實例套上 f 形成新的實例,對新的實例套用Y的黑盒,能夠找到步驟一中的答案。
注:在三的一些實例中細(xì)品。
二:算法思想
貪心
概念:在對問題求解時,總是做出在當(dāng)前看來是最好的選擇。
貪心的證明:先假設(shè)貪心算法得到的解不是最優(yōu)解,假設(shè)S1是貪心算法得到的解,而S2是所有最優(yōu)解中和S1具有最多相同元素的解,然后比較S1和S2,觀察S1和S2中第一個(最前面一個)不一樣的元素,然后在貪心解S2中將不一樣的元素?fù)Q成S1中的那個元素得到另一個最優(yōu)解S3,這樣S3和S1比S2和S1有更多相同元素,和假設(shè)S2是與S1有最多相同元素的最優(yōu)解矛盾,這樣來推導(dǎo)S1是最優(yōu)解。
我的理解:假設(shè)這個不是最優(yōu)的,但是一定存在一個最優(yōu)的解在某一個位置之前和我當(dāng)前解結(jié)構(gòu)是一樣的,那么在這個位置,選最優(yōu)解也可以選當(dāng)前解,不影響最終答案。
[注]概念很簡單,但是實際操作的時候,貪心的角度很重要,同樣的貪心,方向?qū)α耍惴ň褪菍Φ摹?/p>
例子:
interval scheduling
給你一系列活動,每個活動有一個起始時間和一個結(jié)束時間,要求在活動不沖突的情況下找到一種有最多活動的安排。
對于這個問題,我們有一下幾種貪心的角度:
①將任務(wù)按照開始時間升序排列。
②將任務(wù)按照結(jié)束時間升序排列。
③將任務(wù)按照任務(wù)時長升序排列。
④對于每一個任務(wù),都記錄與其他任務(wù)沖突的數(shù)量,按照沖突數(shù)量的升序排列。
其中1,3,4都是不可以的。
任務(wù)結(jié)束時間的貪心證明(反證法):
假設(shè)貪心不是最最優(yōu)的,那我們在最優(yōu)解中找一個與當(dāng)前解有最相似的解。
由圖可以知道,貪心貪的就是最早結(jié)束,所以如果不是最優(yōu),那么最優(yōu)的結(jié)束時間一定晚于貪心的結(jié)束時間。
由上圖就可以證明。
網(wǎng)絡(luò)流
最大流通常與最小割相聯(lián)系。
f 為任意一個流,cap為容量,對于任意的s-t割出來的點集(A,B),v( f ) <= cap(A, B)。
當(dāng)流增加到與割的容量相等時候,就不可能再有增長空間了,稱為最大流。
對于割的容量來說,不同的割法會有不同流量,有些割法永遠(yuǎn)不會有流達(dá)到,比如部分A = {s}, B = {V - s},這種把源點割出來的割法。
綜上,通過這種感性的認(rèn)識,如果能找到一個最小的割,那么這個割就一定是最大能跑到的流(如果流能更高的話在這個割上就會超過容量,反證。)
上圖為一條增廣路,一條增廣路即為一條s-t的路徑,在路徑上仍有流可以跑,其曾廣的流就是該條路徑上最小的剩余容量。(相當(dāng)于每找一條增廣路,就至少有一條邊達(dá)到滿流。)
直到在圖中找不到增廣路,此時已經(jīng)達(dá)到了最大流。
找ST集合:把滿流的邊去掉,從S出發(fā)走到能到的點,遍歷的點就是S集合;剩下的點就屬于T集合。注意,如果找到了在找S集合的時候找到了T點,說明還可以繼續(xù)找增廣路。
[補(bǔ)]有一個很有趣的延伸,如多源點多終點問題。問:如果我有兩個源點s1,s2,兩個終點t1,t2,我想求一組流,使得s1-t1,s2-t2的流達(dá)到最大,是否可以加一個源點S,S與s1,s2相連,邊流無限大;加一個終點T,T與t1,t2相連,邊流無限大,然后這組ST的最大流即可。——答案是No,無法保證是s1-t1,s2-t2,有可能交錯。
動態(tài)規(guī)劃和分治
例子講的感覺不是特別好,對理解感覺起不到很大作用,希望以后有新的想法后進(jìn)行補(bǔ)充。
三:算法的經(jīng)典問題
規(guī)約
規(guī)約是一個重要的概念和思想。
獨立集和點覆蓋:
一個圖的 最大獨立集 與 最小點覆蓋 是不相交的兩個點集,它們的并就是整個點集。
個人理解:獨立集和點覆蓋都是從點的角度進(jìn)行劃分的,如果我們從邊的角度來看,①一個最小的點覆蓋即為我集合中的每一個點都盡可能與更多的邊相連,②同時,一條邊的兩個端點中,只能有一個端點在最小點覆蓋中[下注]
[注]我們假設(shè)有一條邊兩個端點(u,v)都在點覆蓋之中,首先顯然u,v都不是端點,因為假設(shè)u是端點的話只需要選擇v即可;
集合覆蓋 <=p 點覆蓋
給一個集合S和一堆S的子集S1,S2,...,Sm,問是否存在存在k個子集,使它們的并集為S。
構(gòu)造:
集合為點,集合中的元素為邊,有相同元素的邊相連。(注意如果某一元素只在一個子集中出現(xiàn),應(yīng)該怎么處理呢!)
規(guī)約:在構(gòu)造的圖中找最小的點覆蓋,選中的點能覆蓋所有的邊即為對應(yīng)集合的并集能包含所有的元素。所以就完成了集合覆蓋到點覆蓋的規(guī)約。
3SAT <= 獨立集
構(gòu)造:每個句子構(gòu)造一個三角形,把對應(yīng)變量但是相反取值的點相連。
規(guī)約:3SAT的有一個特點就是,每一個句子中至少有一個為真即可,每個句子都必須是真。將相同變量相反取值相連的目的就是,在最大獨立集中,比如選擇x為真,則剩下所有句子中x-ba一定不會被選中,同時由獨立集和構(gòu)造出來三角形的性質(zhì)可以知道,每一個句子,有且僅有一個會被選中(為真)。如上圖,x1-ba為真,x2-ba和x3任選一個為真即可滿足。
自規(guī)約
search problem <=p decision version
比如:如果能在多項式時間內(nèi)找到一個哈密爾頓圈,那么就能在多項式時間內(nèi)找到一個哈密爾頓圈(刪邊)
在此再談P和NP:
我們知道有些問題是可以從搜索問題規(guī)約到判斷問題的,也就是所該問題如果能在多項式內(nèi)判斷,那么就能在多項式中搜索到,那么我們只需要說,這個判斷問題能在多項式時間內(nèi)求解,就叫做P問題,也就是上圖紅字的意思;那NP問題呢,必須要給出一個解的實例,判斷的是這個實例是否滿足求解問題,這個才是上圖中的紅字。比如,我如果能在多項式時間內(nèi)判斷哈密爾頓圈是否(Yes/No)存在,那這個就是ploy-time algorithm,如果我給出了一系列點,能過多項式時間內(nèi)判斷這些點能否構(gòu)成哈密爾頓圈,那這個就是poly-time certifier。
有向哈密爾頓圈規(guī)約到無向哈密爾頓圈
構(gòu)造:把一個點拆分成三個點。
3SAT規(guī)約到有向哈密爾頓圈
構(gòu)造:(下面兩個圖要連在一起看)
從行的角度看,一行代表一個變量;從列的角度來看,每三列代表一個句子。兩邊中一邊是兩個點,一邊是一個點,所以有k個句子的話,每一行有3k+3個節(jié)點。從哈密爾頓圈的答案轉(zhuǎn)到3SAT的答案看這個圈在每一行是從左到右還是從右到左。
3SAT規(guī)約到子集和問題
子集和問題:給一個集合S,問是否能在集合中選取元素,使得總和為W。
構(gòu)造:如下圖,按照前六行和前三列進(jìn)行分割,可以分成4部分,其中1,3,4部分是固定的,即在第一部分,變量v列和 變量為v(包括變量及取反)的行對應(yīng)的格子為0,其余為0;第三部分全為0;第四部分按照12依次寫下來。第二部分,如果Ci句子中有變量v,則記為1,因為一個句子只有三個變量,可以簡單通過第二部分每一列和為3進(jìn)行判定。此時集合已經(jīng)構(gòu)造出來,W為111444,與上面的規(guī)約相似,可以通過3SAT的簡單性質(zhì)進(jìn)行感性的認(rèn)知。
近似
近似的想法很簡單,要解決一個問題,我們希望能夠做到①求解結(jié)果是最優(yōu)的 ②在多項式時間內(nèi)解決 ③對于任意的實例都能夠通過該算法解決。現(xiàn)在對于部分問題,無法完全滿足以上要求,所以就犧牲了①,但是我們希望結(jié)果不是盲目的,所以就引入了近似的概念。
近似算法。比如2-近似,認(rèn)為W為近似解,W為最優(yōu)解,在求最小值的情況下W<=2W;在求最大值的情況下,W>=1/2W*
負(fù)載均衡問題
給m個機(jī)器和n個任務(wù),每個任務(wù)有一個ti的執(zhí)行時間,我們認(rèn)為完成最后一個任務(wù)所需的時間為負(fù)載時間,希望能夠讓這個負(fù)載時間最短。
第一種:將任務(wù)依次放在機(jī)器上,當(dāng)某個機(jī)器空閑時立即放入新任務(wù)。此時是2近似的。
證明:
引理1.最短時間安排是大于等于任務(wù)中時間最長的任務(wù),L* >= max tj
引理2.最短時間安排也是大于等于所有任務(wù)的平均時間,我們在考慮放入最后一個任務(wù)前,根據(jù)我們放置的規(guī)則,該機(jī)器是耗時最短,也就是說,該機(jī)器此時的用時是低于除掉最后一個任務(wù)后的平均時長,更低于所有任務(wù)的平均時長(引理2);再根據(jù)引理1,最后一個任務(wù)應(yīng)該是小于最優(yōu)解的。
補(bǔ)充:
在這里,我還想討論一下這個近似算法的中等于符號,先上結(jié)論:等號不一定能夠找到一個實例,但是可以構(gòu)造出一種結(jié)構(gòu),通過取極限求得,我們認(rèn)為這樣 也算是緊的。
構(gòu)造實例:有m個機(jī)器,其中m(m-1)個任務(wù)的用時為1,1個任務(wù)的用時為m。肯定有一種任務(wù)集合,可以按照以下方式進(jìn)行安排,此時的貪心解為19。
此時最佳的解為10,如下圖:
通過推廣可以知道此時的比為(2m-1)/m,當(dāng)m取極限,能夠達(dá)到2倍。
第二種:將任務(wù)從大到小排序,然后依次放在機(jī)器上,當(dāng)某個機(jī)器空閑時立即放入新任務(wù)。此時是2近似的。
引理3:如果有大于m個任務(wù),那么L*>=2t(m-1)。證明:t(m+1)是目前最短的任務(wù),且目前所有機(jī)器上都有任務(wù)了,所以該任務(wù)加入時最優(yōu)的情況不過是加入設(shè)備的原有任務(wù)剛好和t(m+1)相等,即等號。
中心點選取
(2近似)在n個點中,選取k個中心點,使得這些中心點能夠以半徑R的圓包含所有的點,讓其中最大的半徑最小,如下圖所示:
基礎(chǔ):距離需要滿足的三個定理①(同一性)dist(x, x) = 0 ②(自反)dist(x, y) = dist(y, x) ③(三角不等式)dist(x, y) <=dist(x, z)+dist(z, y)
r(C)為C集合中所有點的最大覆蓋半徑。(需要求min r(C))
算法:在點集中任選一個作為中心點,然后重復(fù)以下步驟k-1次:選取距離已選點集中最遠(yuǎn)的點,加入點集。
證明:先假設(shè)r(C)< 1/2 * r(C)以選好的點畫半徑為1/2 * r(C)的圓,顯然可知[注],這個圓里有且僅有一個r(C)中的點。那么根據(jù)在下圖中,根據(jù)三角不等式可以得出:
[注]在每個點上r(c)一定會包含到c點,而r(C)<1/2 * r(C),相當(dāng)于大圓套小圓,所以c*一定在c的圓中。
帶權(quán)最小點覆蓋——pricing method
(2近似)問題還是很好理解的,在點上加權(quán)值,要找一個點覆蓋,使得權(quán)值最小。如下圖左邊就是一個帶權(quán)的最小點覆蓋。
算法: 任選一條邊(i, j)加上代價,這個代價從零開始,且這個代價的最大值低于i和j節(jié)點的權(quán)值。顯然,這個邊權(quán)值的最大值取決于兩個端點權(quán)值的最小值,我們認(rèn)為當(dāng)邊權(quán)值與點權(quán)值相等時,對應(yīng)的那個點是緊的。把所有緊的點找出來即為點覆蓋。
流程:
證明:
引理:邊權(quán)之和小于等于點覆蓋的點權(quán)之和。這主要是由于涉及到一條邊上兩個點都被選(緊的)的情況,感性認(rèn)知可以看上圖,縮放證明如下:
w(S)是等于所選的節(jié)點的權(quán)值之和的,等于所選節(jié)點節(jié)點所對應(yīng)的邊權(quán)之和,可以把它放大到所有節(jié)點對應(yīng)邊權(quán)之和,這樣因為一條邊(u, v)在u上算過一次后還要在v上算一次,所以等于邊權(quán)和的兩倍。再由上面引理可得。
帶權(quán)最小點覆蓋——線性解法
主要為了線性規(guī)劃和整數(shù)規(guī)劃。
(2近似)沒啥好說的,只需要把方程構(gòu)造出來就行了。
由于求解出來結(jié)果不一定是整數(shù),所以我們認(rèn)為某一點的值大于1/2,就選入點集。
證明:
因為xi+xj >=1,且都是正數(shù),那必至少一個點是大于1/2的(反證,兩個都小于1/2則和小于1)。
背包問題
給你n個物品和一個背包,每個物品有一個價值v和一個大小w,背包的容量是W,要求讓背包裝下盡可能大價值。
背包的時間復(fù)雜度:O(nW)
注意其中n表示物品的個數(shù),無論是1個還是999個,他都是多項式的,這個很好理解。但是W就不一樣了,這是一個數(shù)字。我理解的是這個數(shù)字會很奇特,比如1.00001,比如99999,這些有可能看起來不大但是實際在處理的時候很難處理的數(shù)字,統(tǒng)一的來說,如果我們把這些數(shù)字放在電腦上,都會以二進(jìn)制的方式存儲起來,有些數(shù)字用十進(jìn)制表示很小,但是放在二進(jìn)制上面就會很大,由W導(dǎo)致不能在多項式時間內(nèi)解決(找不到一個范圍/上界來框它)。
算法: 為了處理這個問題,我們改動了dp的狀態(tài)轉(zhuǎn)移方程,要讓這個轉(zhuǎn)移方程和W無關(guān)[注]。
此時還不是多項式的,然后我們再對value進(jìn)行約。[注]
[注]這兩步中,我們把w改成v,并對v進(jìn)行近似處理。OPT的含義變成了,在面對是否選擇第i個物品時,要想讓價值達(dá)到當(dāng)前值,最少的weight。理由是更改后的誤差是可以忍受的:對v進(jìn)行近似,結(jié)果只會出現(xiàn)最大價值的上下誤差,如果對w進(jìn)行近似,則有可能出現(xiàn)該物品不能放入背包中,導(dǎo)致整個物品直接放棄的情況。
證明: