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)。

二、【動態規劃法】

最優化原理

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列開始,以行或者列優先的順序,依次填寫表格,最後根據整個表格的資料通過簡單的取捨或者運算求得問題的最優解。下面分別以求解最大化投資回報問題和最長公共子序列問題為例闡述用動態規劃演算法求解問題的一般思路。

三、【貪心演算法】

所謂貪心演算法是指,在對問題求解時,總是做出在當前看來是最好的選擇。也就是說,不從整體最優上加以考慮,他所做出的僅是在某種意義上的區域性最優解。心演算法不是對所有問題都能得到整體最優解,但對範圍相當廣泛的許多問題他能產生整體最優解或者是整體最優解的近似解。

貪心演算法的基本思路如下:

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.汽車加油問題之貪心演算法

四、【回溯法】

回溯法是一種選優搜尋法,按選優條件向前搜尋,以達到目標。但當探索到某一步時,發現原先選擇並不優或達不到目標,就退回一步重新選擇,這種走不通就退回再走的技術為回溯法,而滿足回溯條件的某個狀態的點稱為“回溯點”。

1、回溯法的一般描述

可用回溯法求解的問題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個數的全排列

五、【分支限界法】

基本思想 :分支限界法常以廣度優先或以最小耗費(最大效益)優先的方式搜尋問題的解空間樹。

在分支限界法中,每一個活結點只有一次機會成為擴充套件結點。活結點一旦成為擴充套件結點,就一次性產生其所有兒子結點。在這些兒子結點中,導致不可行解或導致非最優解的兒子結點被捨棄,其餘兒子結點被加入活結點表中。 此後,從活結點表中取下一結點成為當前擴充套件結點,並重覆上述結點擴充套件過程。這個過程一直持續到找到所需的解或活結點表為空時為止。

常見的兩種分支限界法:

(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. 分支限界法之旅行售貨員問題


原文:https://blog.csdn.net/zolalad/article/details/11393915