五大常用演算法——分治法,動態規劃,回溯法,分支界限法,貪心演算法
分治演算法
一、基本概念
在電腦科學中,分治法是一種很重要的演算法。字面上的解釋是“分而治之”,就是把一個複雜的問題分成兩個或更多的相同或相似的子問題,再把子問題分成更小的子問題……直到最後子問題可以簡單的直接求解,原問題的解即子問題的解的合并。這個技巧是很多高效演算法的基礎,如排序演算法(快速排序,歸併排序),傅立葉變換(快速傅立葉變換)……
任何一個可以用電腦求解的問題所需的計算時間都與其規模有關。問題的規模越小,越容易直接求解,解題所需的計算時間也越少。例如,對於n個元素的排序問題,當n=1時,不需任何計算。n=2時,只要作一次比較即可排好序。n=3時只要作3次比較即可,…。而當n較大時,問題就不那麼容易處理了。要想直接解決一個規模較大的問題,有時是相當困難的。
--------------------------------------------------------------------------------
二、基本思想及策略
分治法的設計思想是:將一個難以直接解決的大問題,分割成一些規模較小的相同問題,以便各個擊破,分而治之。
分治策略是:對於一個規模為n的問題,若該問題可以容易地解決(比如說規模n較小)則直接解決,否則將其分解為k個規模較小的子問題,這些子問題互相獨立且與原問題形式相同,遞迴地解這些子問題,然後將各子問題的解合并得到原問題的解。這種演算法設計策略叫做分治法。
如果原問題可分割成k個子問題,1<k≤n,且這些子問題都可解並可利用這些子問題的解求出原問題的解,那麼這種分治法就是可行的。由分治法產生的子問題往往是原問題的較小模式,這就為使用遞迴技術提供了方便。在這種情況下,反覆應用分治手段,可以使子問題與原問題類型一致而其規模卻不斷縮小,最終使子問題縮小到很容易直接求出其解。這自然導致遞迴過程的產生。分治與遞迴像一對孿生兄弟,經常同時應用在演算法設計之中,並由此產生許多高效演算法。
--------------------------------------------------------------------------------
三、分治法適用的情況
分治法所能解決的問題一般具有以下幾個特徵:
1) 該問題的規模縮小到一定的程度就可以容易地解決
2) 該問題可以分解為若干個規模較小的相同問題,即該問題具有最優子結構性質。
3) 利用該問題分解出的子問題的解可以合并為該問題的解;
4) 該問題所分解出的各個子問題是相互獨立的,即子問題之間不包含公用的子子問題。
第一條特徵是絕大多數問題都可以滿足的,因為問題的計算複雜性一般是隨著問題規模的增加而增加;
第二條特徵是應用分治法的前提它也是大多數問題可以滿足的,此特徵反映了遞迴思想的應用;、
第三條特徵是關鍵,能否利用分治法完全取決於問題是否具有第三條特徵,如果具備了第一條和第二條特徵,而不具備第三條特徵,則可以考慮用貪進法或動態規劃法。
第四條特徵涉及到分治法的效率,如果各子問題是不獨立的則分治法要做許多不必要的工作,重複地解公用的子問題,此時雖然可用分治法,但一般用動態規劃法較好。
--------------------------------------------------------------------------------
四、分治法的基本步驟
分治法在每一層遞迴上都有三個步驟:
step1 分解:將原問題分解為若干個規模較小,相互獨立,與原問題形式相同的子問題;
step2 解決:若子問題規模較小而容易被解決則直接解,否則遞迴地解各個子問題
step3 合并:將各個子問題的解合并為原問題的解。
它的一般的演算法設計模式如下:
Divide-and-Conquer(P)
1. if |P|≤n0
2. then return(ADHOC(P))
3. 將P分解為較小的子問題 P1 ,P2 ,...,Pk
4. for i←1 to k
5. do yi ← Divide-and-Conquer(Pi) △ 遞迴解決Pi
6. T ← MERGE(y1,y2,...,yk) △ 合并子問題
7. 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的解。
--------------------------------------------------------------------------------
五、分治法的複雜性分析
一個分治法將規模為n的問題分成k個規模為n/m的子問題去解。設分解閥值n0=1,且adhoc解規模為1的問題耗費1個單位時間。再設將原問題分解為k個子問題以及用merge將k個子問題的解合并為原問題的解需用f(n)個單位時間。用T(n)表示該分治法解規模為|P|=n的問題所需的計算時間,則有:
T(n)= k T(n/m)+f(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)。
--------------------------------------------------------------------------------
六、可使用分治法求解的一些經典問題
(1)二分搜尋
(2)大整數乘法
(3)Strassen矩陣乘法
(4)棋盤覆蓋
(5)合并排序
(6)快速排序
(7)線性時間選擇
(8)最接近點對問題
(9)迴圈賽議程表
(10)漢諾塔
--------------------------------------------------------------------------------
七、依據分治法設計程式時的思維過程
實際上就是類似於數學歸納法,找到解決本問題的求解方程公式,然後根據方程公式設計遞迴程式。
1、一定是先找到最小問題規模時的求解方法
2、然後考慮隨著問題規模增大時的求解方法
3、找到求解的遞迴函式式後(各種規模或因子),設計遞迴程式即可。
五大常用演算法之二:動態規划算法
一、基本概念
動態規划過程是:每次決策依賴於目前狀態,又隨即引起狀態的轉移。一個決策序列就是在變化的狀態中產生出來的,所以,這種多階段最佳化決策解決問題的過程就稱為動態規劃。
二、基本思想與策略
基本思想與分治法類似,也是將待求解的問題分解為若干個子問題(階段),按順序求解子階段,前一子問題的解,為後一子問題的求解提供了有用的資訊。在求解任一子問題時,列出各種可能的局部解,通過決策保留那些有可能達到最優的局部解,丟棄其他局部解。依次解決各子問題,最後一個子問題就是初始問題的解。
由於動態規劃解決的問題多數有重疊子問題這個特點,為減少重複計算,對每一個子問題只解一次,將其不同階段的不同狀態儲存在一個二維數組中。
與分治法最大的差別是:適合於用動態規劃法求解的問題,經分解後得到的子問題往往不是互相獨立的(即下一個子階段的求解是建立在上一個子階段的解的基礎上,進行進一步的求解)。
--------------------------------------------------------------------------------
三、適用的情況
能採用動態規劃求解的問題的一般要具有3個性質:
(1) 最佳化原理:如果問題的最優解所包含的子問題的解也是最優的,就稱該問題具有最優子結構,即滿足最佳化原理。
(2) 無後效性:即某階段狀態一旦確定,就不受這個狀態以後決策的影響。也就是說,某狀態以後的過程不會影響以前的狀態,只與目前狀態有關。
(3)有重疊子問題:即子問題之間是不獨立的,一個子問題在下一階段決策中可能被多次使用到。(該性質並不是動態規劃適用的必要條件,但是如果沒有這條性質,動態規划算法同其他演算法相比就不具備優勢)
--------------------------------------------------------------------------------
四、求解的基本步驟
動態規劃所處理的問題是一個多階段決策問題,一般由初始狀態開始,通過對中間階段決策的選擇,達到結束狀態。這些決策形成了一個決策序列,同時確定了完成整個過程的一條活動路線(通常是求最優的活動路線)。如圖所示。動態規劃的設計都有著一定的模式,一般要經曆以下幾個步驟。
初始狀態→│決策1│→│決策2│→…→│決策n│→結束狀態
圖1 動態規劃決策過程示意圖
(1)劃分階段:按照問題的時間或空間特徵,把問題分為若干個階段。在劃分階段時,注意劃分後的階段一定要是有序的或者是可排序的,否則問題就無法求解。
(2)確定狀態和狀態變數:將問題發展到各個階段時所處於的各種客觀情況用不同的狀態表示出來。當然,狀態的選擇要滿足無後效性。
(3)確定決策並寫出狀態轉移方程:因為決策和狀態轉移有著天然的聯絡,狀態轉移就是根據上一階段的狀態和決策來匯出本階段的狀態。所以如果確定了決策,狀態轉移方程也就可寫出。但事實上常常是反過來做,根據相鄰兩個階段的狀態之間的關係來確定決策方法和狀態轉移方程。
(4)尋找邊界條件:給出的狀態轉移方程是一個遞推式,需要一個遞推的終止條件或邊界條件。
一般,只要解決問題的階段、狀態和狀態轉移決策確定了,就可以寫出狀態轉移方程(包括邊界條件)。
實際應用中可以按以下幾個簡化的步驟進行設計:
(1)分析最優解的性質,並刻畫其結構特徵。
(2)遞迴的定義最優解。
(3)以自底向上或自頂向下的記憶化方式(備忘錄法)計算出最優值
(4)根據計算最優值時得到的資訊,構造問題的最優解
--------------------------------------------------------------------------------
五、演算法實現的說明
動態規劃的主要痛點在於理論上的設計,也就是上面4個步驟的確定,一旦設計完成,實現部分就會非常簡單。
使用動態規劃求解問題,最重要的就是確定動態規劃三要素:
(1)問題的階段 (2)每個階段的狀態
(3)從前一個階段轉化到後一個階段之間的遞推關係。
遞推關係必須是從次小的問題開始到較大的問題之間的轉化,從這個角度來說,動態規劃往往可以用遞迴程式來實現,不過因為遞推可以充分利用前面儲存的子問題的解來減少重複計算,所以對於大規模問題來說,有遞迴不可比擬的優勢,這也是動態規划算法的核心之處。
確定了動態規劃的這三要素,整個求解過程就可以用一個最優決策表來描述,最優決策表是一個二維表,其中行表示決策的階段,列表示問題狀態,表格需要填寫的資料一般對應此問題的在某個階段某個狀態下的最優值(如最短路徑,最長公用子序列,最大價值等),填表的過程就是根據遞推關係,從1行1列開始,以行或者列優先的順序,依次填寫表格,最後根據整個表格的資料通過簡單的取捨或者運算求得問題的最優解。
五大常用演算法之三:回溯法
1、概念
回溯演算法實際上一個類似枚舉的搜尋嘗試過程,主要是在搜尋嘗試過程中尋找問題的解,當發現已不滿足求解條件時,就“回溯”返回,嘗試別的路徑。
回溯法是一種選優搜尋法,按選優條件向前搜尋,以達到目標。但當探索到某一步時,發現原先選擇並不優或達不到目標,就退回一步重新選擇,這種走不通就退回再走的技術為回溯法,而滿足回溯條件的某個狀態的點稱為“回溯點”。
許多複雜的,規模較大的問題都可以使用回溯法,有“通用解題方法”的美稱。
2、基本思想
在包含問題的所有解的解空間樹中,按照深度優先搜尋的策略,從根結點出發深度探索解空間樹。當探索到某一結點時,要先判斷該結點是否包含問題的解,如果包含,就從該結點出發繼續探索下去,如果該結點不包含問題的解,則逐層向其祖先結點回溯。(其實回溯法就是對隱式圖的深度優先搜尋演算法)。
若用回溯法求問題的所有解時,要回溯到根,且根結點的所有可行的子樹都要已被搜尋遍才結束。
而若使用回溯法求任一個解時,只要搜尋到問題的一個解就可以結束。
3、用回溯法解題的一般步驟:
(1)針對所給問題,確定問題的解空間:
首先應明確定義問題的解空間,問題的解空間應至少包含問題的一個(最優)解。
(2)確定結點的擴充搜尋規則