算法設計之五大經常使用算法設計方法總結

算法設計之五大經常使用算法設計方法總結

 

來源 http://blog.csdn.net/zolalad/article/details/11393915html

 

1、【分治法】
 在計算機科學中,分治法是一種很重要的算法。字面上的解釋是「分而治之」,就是把一個複雜的問題分紅兩個或更多的相同或類似的子問題,再把子問題分紅更小的子問題……直到最後子問題能夠簡單的直接求解,原問題的解即子問題的解的合併。這個技巧是不少高效算法的基礎,如排序算法(快速排序,歸併排序),傅立葉變換(快速傅立葉變換)……等。任何一個能夠用計算機求解的問題所需的計算時間都與其規模有關。問題的規模越小,越容易直接求解,解題所需的計算時間也越少。例如,對於n個元素的排序問題,當n=1時,不需任何計算。n=2時,只要做一次比較便可排好序。n=3時只要做3次比較便可,…。而當n較大時,問題就不那麼容易處理了。要想直接解決一個規模較大的問題,有時是至關困難的。
分治法的設計思想是,將一個難以直接解決的大問題,分割成一些規模較小的相同問題,以便各個擊破,分而治之
分治策略是:對於一個規模爲n的問題,若該問題能夠容易地解決(好比說規模n較小)則直接解決,不然將其分解爲k個規模較小的子問題,這些子問題互相獨立且與原問題形式相同,遞歸地解這些子問題,而後將各子問題的解合併獲得原問題的解。這種算法設計策略叫作分治法
若是原問題可分割成k(1<k≤n)個子問題, 且這些子問題均可解並可利用這些子問題的解求出原問題的解,那麼這種分治法就是可行的。由分治法產生的子問題每每是原問題的較小模式,這就爲使用"遞歸技術"提供了方便。在這種狀況下,反覆應用分治手段,可使子問題與原問題類型一致而其規模卻不斷縮小,最終使子問題縮小到很容易直接求出其解,這天然致使遞歸過程的產生。"分治"與"遞歸"像一對孿生兄弟,常常同時應用在算法設計之中,並由此產生許多高效算法
分治法所能解決的問題通常具備如下幾個特徵:
1) 該問題的規模縮小到必定的程度就能夠容易地解決
2) 該問題能夠分解爲若干個規模較小的相同問題,即該問題具備最優子結構性質。(前提)
3) 利用該問題分解出的子問題的解能夠合併爲該問題的解;(最關鍵的一點)
4) 該問題所分解出的各個子問題是相互獨立的,即子問題之間不包含公共的子子問題
上述的第一條特徵是絕大多數問題均可以知足的,由於問題的計算複雜性通常是隨着問題規模的增長而增長;第二條特徵是應用分治法的前提它也是大多數問題能夠知足的,此特徵反映了遞歸思想的應用;第三條特徵是關鍵,可否利用分治法徹底取決於問題是否具備第三條特徵,若是具有了第一條和第二條特徵,而不具有第三條特徵,則能夠考慮用貪心法或動態規劃法。第四條特徵涉及到分治法的效率若是各子問題是不獨立的則分治法要作許多沒必要要的工做,重複地解公共的子問題,此時雖然可用分治法,但通常用動態規劃法較好。
分治法的基本步驟
分治法在每一層遞歸上都有三個步驟:
1)分解:將原問題分解爲若干個規模較小,相互獨立,與原問題形式相同的子問題;
2)解決:若子問題規模較小而容易被解決則直接解,不然遞歸地解各個子問題
3)合併:將各個子問題的解合併爲原問題的解。
它的通常的算法設計模式以下:
Divide-and-Conquer(P)
if |P|≤n0 {
       then return(ADHOC(P))
}else{
         將P繼續分解爲較小的子問題 P1 ,P2 ,...,Pk
     for i←1 to k
     do yi ← Divide-and-Conquer(Pi) △ 遞歸解決Pi
     T ← MERGE(y1,y2,...,yk) △ 合併子問題
}
return(T)
其中|P|表示問題P的規模;n0爲一閾值,表示當問題P的規模不超過n0時,問題已容易直接解出,沒必要再繼續分解。ADHOC(P)是該分治法中的基本子算法,用於直接解小規模的問題P。所以,當P的規模不超過n0時直接用算法ADHOC(P)求解。算法MERGE(y1,y2,...,yk)是該分治法中的合併子算法,用於將P的子問題P1 ,P2 ,...,Pk的相應的解y1,y2,...,yk合併爲P的解。
分治法的應用:1.遞歸與HANOI塔問題; 2.二分法求方程近似解    3. 用C++實現合併排序   4.求最大值和最小值的分治算法
分治法的複雜性分析
一個分治法將規模爲n的問題分紅k個規模爲n/m的子問題去解。設分解閥值n0=1,且adhoc解規模爲1的問題耗費1個單位時間。再設將原問題分解爲k個子問題以及用merge將k個子問題的解合併爲原問題的解需用f(n)個單位時間。用T(n)表示該分治法解規模爲|P|=n的問題所需的計算時間,則有: 

經過迭代法求得方程的解: 

遞歸方程及其解只給出n等於m的方冪時T(n)的值,可是若是認爲T(n)足夠平滑,那麼由n等於m的方冪時T(n)的值能夠估計T(n)的增加速度。一般假定T(n)是單調上升的,從而當mi≤n<mi+1時,T(mi)≤T(n)<T(mi+1)。
2、【動態規劃法】
最優化原理
1951年美國數學家R.Bellman等人,根據一類多階段問題的特色,把「多階段決策」問題變換爲一系列互相聯繫的「單階段問題」,而後逐個加以解決。並且一些靜態模型,只要人爲地引進「時間」因素,分紅時段,就能夠轉化成多階段的動態模型,用動態規劃方法去處理。與此同時,他提出瞭解決這類問題的「最優化原理」(Principle of optimality):「一個過程的最優決策具備這樣的性質:即不管其初始狀態和初始決策如何,其從此諸策略對以第一個決策所造成的狀態做爲初始狀態的過程而言,必須構成最優策略」。簡言之,一個最優策略的子策略,對於它的初態和終態而言也必是最優的。這個「最優化原理」若是用數學化一點的語言來描述的話,就是:假設爲了解決某一優化問題,須要依次做出n個決策D1,D2,…,Dn,如若這個決策序列是最優的,對於任何一個整數k,1 < k < n,不論前面k個決策是怎樣的,之後的最優決策只取決於由前面決策所肯定的當前狀態,即之後的決策Dk+1,Dk+2,…,Dn也是最優的。
最優化原理是動態規劃的基礎,任何一個問題,若是失去了這個最優化原理的支持,就不可能用動態規劃方法計算。能採用動態規劃求解的問題都須要知足必定的條件:
(1) 問題中的狀態必須知足最優化原理;
(2) 問題中的狀態必須知足無後效性。
所謂的無後效性是指:「下一時刻的狀態只與當前狀態有關,而和當前狀態以前的狀態無關,當前的狀態是對以往決策的總結」。
問題求解模式
動態規劃所處理的問題是一個多階段決策問題,通常由初始狀態開始,經過對中間階段決策的選擇,達到結束狀態。這些決策造成了一個決策序列,同時肯定了完成整個過程的一條活動路線(一般是求最優的活動路線)。如圖所示。動態規劃的設計都有着必定的模式,通常要經歷如下幾個步驟。
初始狀態→│決策1│→│決策2│→…→│決策n│→結束狀態
圖1 動態規劃決策過程示意圖
(1)劃分階段:按照問題的時間或空間特徵,把問題分爲若干個階段。在劃分階段時,注意劃分後的階段必定要是有序的或者是可排序的,不然問題就沒法求解。
(2)肯定狀態和狀態變量:將問題發展到各個階段時所處於的各類客觀狀況用不一樣的狀態表示出來。固然,狀態的選擇要知足無後效性。
(3)肯定決策並寫出狀態轉移方程:由於決策和狀態轉移有着自然的聯繫,狀態轉移就是根據上一階段的狀態和決策來導出本階段的狀態。因此若是肯定了決策,狀態轉移方程也就可寫出。但事實上經常是反過來作,根據相鄰兩段各狀態之間的關係來肯定決策。
(4)尋找邊界條件:給出的狀態轉移方程是一個遞推式,須要一個遞推的終止條件或邊界條件。
動態規劃法的應用:1.動態規劃求0/1揹包問題      2.最長公共子串問題的實現 3. 用動態規劃實現導彈攔截      4.最大化投資回報問題的實現
算法實現
動態規劃的主要難點在於理論上的設計,也就是上面4個步驟的肯定,一旦設計完成,實現部分就會很是簡單。使用動態規劃求解問題,最重要的就是肯定動態規劃三要素:問題的階段,每一個階段的狀態以及從前一個階段轉化到後一個階段之間的遞推關係。遞推關係必須是從次小的問題開始到較大的問題之間的轉化,從這個角度來講,動態規劃每每能夠用遞歸程序來實現,不過由於遞推能夠充分利用前面保存的子問題的解來減小重複計算,因此對於大規模問題來講,有遞歸不可比擬的優點,這也是動態規劃算法的核心之處。肯定了動態規劃的這三要素,整個求解過程就能夠用一個最優決策表來描述,最優決策表是一個二維表,其中行表示決策的階段,列表示問題狀態,表格須要填寫的數據通常對應此問題的在某個階段某個狀態下的最優值(如最短路徑,最長公共子序列,最大價值等),填表的過程就是根據遞推關係,從1行1列開始,以行或者列優先的順序,依次填寫表格,最後根據整個表格的數據經過簡單的取捨或者運算求得問題的最優解。下面分別以求解最大化投資回報問題和最長公共子序列問題爲例闡述用動態規劃算法求解問題的通常思路。
3、【貪心算法】
所謂貪心算法是指,在對問題求解時,老是作出在當前看來是最好的選擇。也就是說,不從總體最優上加以考慮,他所作出的僅是在某種意義上的局部最優解。心算法不是對全部問題都能獲得總體最優解,但對範圍至關普遍的許多問題他能產生總體最優解或者是總體最優解的近似解。
貪心算法的基本思路以下:
1.創建數學模型來描述問題。
2.把求解的問題分紅若干個子問題。
3.對每一子問題求解,獲得子問題的局部最優解。
4.把子問題的解局部最優解合成原來解問題的一個解。
實現該算法的過程:
從問題的某一初始解出發;
while 能朝給定總目標前進一步 do
求出可行解的一個解元素;
由全部解元素組合成問題的一個可行解;
下面是一個可使用貪心算法解的題目,貪心解的確不錯,惋惜不是最優解:
例題分析:
[揹包問題]有一個揹包,揹包容量是M=150。有7個物品,物品能夠分割成任意大小。
要求儘量讓裝入揹包中的物品總價值最大,但不能超過總容量。
物品:  A   B   C   D   E    F   G
重量: 35  30  60  50  40  10  25
價值: 10  40  30  50  35  40  30
分析:
目標函數: ∑pi最大
約束條件是裝入的物品總重量不超過揹包容量:∑wi<=M( M=150)
(1)根據貪心的策略,每次挑選價值最大的物品裝入揹包,獲得的結果是否最優?
(2)每次挑選所佔重量最小的物品裝入是否能獲得最優解?
(3)每次選取單位重量價值最大的物品,成爲解本題的策略。
值得注意的是,貪心算法並非徹底不可使用,貪心策略一旦通過證實成立後,它就是一種高效的算法。貪心算法仍是很常見的算法之一,這是因爲它簡單易行,構造貪心策略不是很困難。惋惜的是,它須要證實後才能真正運用到題目的算法中。
通常來講,貪心算法的證實圍繞着:整個問題的最優解必定由在貪心策略中存在的子問題的最優解得來的。
對於例題中的3種貪心策略,都是沒法成立(沒法被證實)的,解釋以下:
    (1)貪心策略:選取價值最大者。反例:
    W=30
    物品:A B C
    重量:28 12 12
    價值:30 20 20
    根據策略,首先選取物品A,接下來就沒法再選取了,但是,選取B、C則更好。
    (2)貪心策略:選取重量最小。它的反例與第一種策略的反例差很少。
    (3)貪心策略:選取單位重量價值最大的物品。反例:
    W=30
    物品:A B C
    重量:28 20 10
    價值:28 20 10
根據策略,三種物品單位重量價值同樣,程序沒法依據現有策略做出判斷,若是選擇A,則答案錯誤。
貪心算法應用:1.最小生成樹之Prim算法  2. 最小生成樹之kruskal算法
3. 貪心算法在揹包中的應用 4.汽車加油問題之貪心算法
4、【回溯法】
回溯法是一種選優搜索法,按選優條件向前搜索,以達到目標。但當探索到某一步時,發現原先選擇並不優或達不到目標,就退回一步從新選擇,這種走不通就退回再走的技術爲回溯法,而知足回溯條件的某個狀態的點稱爲「回溯點」。
一、回溯法的通常描述
可用回溯法求解的問題P,一般要能表達爲:對於已知的由n元組(x1,x2,…,xn)組成的一個狀態空間E={(x1,x2,…,xn)∣xi∈Si ,i=1,2,…,n},給定關於n元組中的一個份量的一個約束集D,要求E中知足D的所有約束條件的全部n元組。其中Si是份量xi的定義域,且 |Si| 有限,i=1,2,…,n。咱們稱E中知足D的所有約束條件的任一n元組爲問題P的一個解。
解問題P的最樸素的方法就是枚舉法,即對E中的全部n元組逐一地檢測其是否知足D的所有約束,若知足,則爲問題P的一個解。但顯然,其計算量是至關大的。
咱們發現,對於許多問題,所給定的約束集D具備完備性,即i元組(x1,x2,…,xi)知足D中僅涉及到x1,x2,…,xi的全部約束意味着j(j<i)元組(x1,x2,…,xj)必定也知足D中僅涉及到x1,x2,…,xj的全部約束,i=1,2,…,n。換句話說,只要存在0≤j≤n-1,使得(x1,x2,…,xj)違反D中僅涉及到x1,x2,…,xj的約束之一,則以(x1,x2,…,xj)爲前綴的任何n元組(x1,x2,…,xj,xj+1,…,xn)必定也違反D中僅涉及到x1,x2,…,xi的一個約束,n≥i>j。所以,對於約束集D具備完備性的問題P,一旦檢測判定某個j元組(x1,x2,…,xj)違反D中僅涉及x1,x2,…,xj的一個約束,就能夠確定,以(x1,x2,…,xj)爲前綴的任何n元組(x1,x2,…,xj,xj+1,…,xn)都不會是問題P的解,於是就沒必要去搜索它們、檢測它們。回溯法正是針對這類問題,利用這類問題的上述性質而提出來的比枚舉法效率更高的算法。
回溯法首先將問題P的n元組的狀態空間E表示成一棵高爲n的帶權有序樹T,把在E中求問題P的全部解轉化爲在T中搜索問題P的全部解。樹T相似於檢索樹,它能夠這樣構造:設Si中的元素可排成xi(1) ,xi(2) ,…,xi(mi-1) ,|Si| =mi,i=1,2,…,n。從根開始,讓T的第I層的每個結點都有mi個兒子。這mi個兒子到它們的雙親的邊,按從左到右的次序,分別帶權xi+1(1) ,xi+1(2) ,…,xi+1(mi) ,i=0,1,2,…,n-1。照這種構造方式,E中的一個n元組(x1,x2,…,xn)對應於T中的一個葉子結點,T的根到這個葉子結點的路徑上依次的n條邊的權分別爲x1,x2,…,xn,反之亦然。另外,對於任意的0≤i≤n-1,E中n元組(x1,x2,…,xn)的一個前綴I元組(x1,x2,…,xi)對應於T中的一個非葉子結點,T的根到這個非葉子結點的路徑上依次的I條邊的權分別爲x1,x2,…,xi,反之亦然。特別,E中的任意一個n元組的空前綴(),對應於T的根。
於是,在E中尋找問題P的一個解等價於在T中搜索一個葉子結點,要求從T的根到該葉子結點的路徑上依次的n條邊相應帶的n個權x1,x2,…,xn知足約束集D的所有約束。在T中搜索所要求的葉子結點,很天然的一種方式是從根出發,按深度優先的策略逐步深刻,即依次搜索知足約束條件的前綴1元組(x1i)、前綴2元組(x1,x2)、…,前綴I元組(x1,x2,…,xi),…,直到i=n爲止。
在回溯法中,上述引入的樹被稱爲問題P的狀態空間樹;樹T上任意一個結點被稱爲問題P的狀態結點;樹T上的任意一個葉子結點被稱爲問題P的一個解狀態結點;樹T上知足約束集D的所有約束的任意一個葉子結點被稱爲問題P的一個回答狀態結點,它對應於問題P的一個解.
用回溯法解題的通常步驟:
(1)針對所給問題,定義問題的解空間;
(2)肯定易於搜索的解空間結構;
(3)以深度優先方式搜索解空間,並在搜索過程當中用剪枝函數避免無效搜索。
回溯法應用:1.回溯法之數的劃分               2.回溯法求解 運動員最佳配對問題 
3.回溯法解決汽車加油次數最少問題 4. 用回溯法找出n個天然數中取r個數的全排列
5、【分支限界法】
基本思想 :分支限界法常以廣度優先或以最小耗費(最大效益)優先的方式搜索問題的解空間樹。 
在分支限界法中,每個活結點只有一次機會成爲擴展結點。活結點一旦成爲擴展結點,就一次性產生其全部兒子結點。在這些兒子結點中,致使不可行解或致使非最優解的兒子結點被捨棄,其他兒子結點被加入活結點表中。 此後,從活結點表中取下一結點成爲當前擴展結點,並重覆上述結點擴展過程。這個過程一直持續到找到所需的解或活結點表爲空時爲止。
常見的兩種分支限界法:
(1)隊列式(FIFO)分支限界法
按照隊列先進先出(FIFO)原則選取下一個節點爲擴展節點。 
(2)優先隊列式分支限界法
按照優先隊列中規定的優先級選取優先級最高的節點成爲當前擴展節點。
分支限界法與回溯法的不一樣:
(1)求解目標:回溯法的求解目標是找出解空間樹中知足約束條件的全部解,而分支限界法的求解目標則是找出知足約束條件的一個解,或是在知足約束條件的解中找出在某種意義下的最優解。 
(2)搜索方式的不一樣:回溯法以深度優先的方式搜索解空間樹,而分支限界法則以廣度優先或以最小耗費優先的方式搜索解空間樹。
解空間樹的動態搜索
(1)回溯求解0/1揹包問題,雖剪枝減小了搜索空間,但整個搜索按深度優先機械進行,是盲目搜索(不可預測本結點如下的結點進行的如何)。
(2)回溯求解TSP也是盲目的(雖有目標函數,也只有找到一個可行解後纔有意義)
(3)分支限界法首先肯定一個合理的限界函數,並根據限界函數肯定目標函數的界[down, up];而後按照廣度優先策略遍歷問題的解空間樹,在某一分支上,依次搜索該結點的全部孩子結點,分別估算這些孩子結點的目標函數的可能取值(對最小化問題,估算結點的down,對最大化問題,估算結點的up)。若是某孩子結點的目標函數值超出目標函數的界,則將其丟棄(今後結點生成的解不會比目前已得的更好),不然入待處理表。
分支限界法的設計思路
設求解最大化問題,解向量爲X=(x1,…,xn),xi的取值範圍爲Si,|Si|=ri。在使用分支限界搜索問題的解空間樹時,先根據限界函數估算目標函數的界[down, up],而後從根結點出發,擴展根結點的r1個孩子結點,從而構成份量x1的r1種可能的取值方式。
對這r1個孩子結點分別估算可能的目標函數bound(x1),其含義:以該結點爲根的子樹全部可能的取值不大於bound(x1),即:
bound(x1)≥bound(x1,x2)≥…≥ bound(x1,…,xn)
若某孩子結點的目標函數值超出目標函數的下界,則將該孩子結點丟棄;不然,將該孩子結點保存在待處理結點表PT中。
再取PT表中目標函數極大值結點做爲擴展的根結點,重複上述。
直到一個葉子結點時的可行解X=(x1,…,xn),及目標函數值bound(x1,…,xn)。
分支限界法應用:1.分支限界法之裝載問題       2. 分支限界法之佈線問題 
3. 分支限界法之0 1揹包問題 4. 分支限界法之旅行售貨員問題
整理參考:http://c.chinaitlab.com/special/algorithm/Index.html 
相關文章
相關標籤/搜索