天天看點

詳解ACM五大常用算法——分治法,動态規劃,回溯法,分支界限法,貪心算法

分治算法

一、基本概念

   在計算機科學中,分治法是一種很重要的算法。字面上的解釋是“分而治之”,就是把一個複雜的問題分成兩個或更多的相同或相似的子問題,再把子問題分成更小的子問題……直到最後子問題可以簡單的直接求解,原問題的解即子問題的解的合并。這個技巧是很多高效算法的基礎,如排序算法(快速排序,歸并排序),傅立葉變換(快速傅立葉變換)……

    任何一個可以用計算機求解的問題所需的計算時間都與其規模有關。問題的規模越小,越容易直接求解,解題所需的計算時間也越少。例如,對于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)确定結點的擴充搜尋規則

    (3)以深度優先方式搜尋解空間,并在搜尋過程中用剪枝函數避免無效搜尋。

分支限界法

一、基本描述

    類似于回溯法,也是一種在問題的解空間樹T上搜尋問題解的算法。但在一般情況下,分支限界法與回溯法的求解目标不同。回溯法的求解目标是找出T中滿足限制條件的所有解,而分支限界法的求解目标則是找出滿足限制條件的一個解,或是在滿足限制條件的解中找出使某一目标函數值達到極大或極小的解,即在某種意義下的最優解。

   (1)分支搜尋算法

    所謂“分支”就是采用廣度優先的政策,依次搜尋E-結點的所有分支,也就是所有相鄰結點,抛棄不滿足限制條件的結點,其餘結點加入活結點表。然後從表中選擇一個結點作為下一個E-結點,繼續搜尋。

     選擇下一個E-結點的方式不同,則會有幾種不同的分支搜尋方式。

   1)FIFO搜尋

   2)LIFO搜尋

   3)優先隊列式搜尋

(2)分支限界搜尋算法  

二、分支限界法的一般過程

    由于求解目标不同,導緻分支限界法與回溯法在解空間樹T上的搜尋方式也不相同。回溯法以深度優先的方式搜尋解空間樹T,而分支限界法則以廣度優先或以最小耗費優先的方式搜尋解空間樹T。

    分支限界法的搜尋政策是:在擴充結點處,先生成其所有的兒子結點(分支),然後再從目前的活結點表中選擇下一個擴充對點。為了有效地選擇下一擴充結點,以加速搜尋的程序,在每一活結點處,計算一個函數值(限界),并根據這些已計算出的函數值,從目前活結點表中選擇一個最有利的結點作為擴充結點,使搜尋朝着解空間樹上有最優解的分支推進,以便盡快地找出一個最優解。

    分支限界法常以廣度優先或以最小耗費(最大效益)優先的方式搜尋問題的解空間樹。問題的解空間樹是表示問題解空間的一棵有序樹,常見的有子集樹和排列樹。在搜尋問題的解空間樹時,分支限界法與回溯法對目前擴充結點所使用的擴充方式不同。在分支限界法中,每一個活結點隻有一次機會成為擴充結點。活結點一旦成為擴充結點,就一次性産生其所有兒子結點。在這些兒子結點中,那些導緻不可行解或導緻非最優解的兒子結點被舍棄,其餘兒子結點被子加入活結點表中。此後,從活結點表中取下一結點成為目前擴充結點,并重複上述結點擴充過程。這個過程一直持續到找到所求的解或活結點表為空時為止。

三、回溯法和分支限界法的一些差別

    有一些問題其實無論用回溯法還是分支限界法都可以得到很好的解決,但是另外一些則不然。也許我們需要具體一些的分析——到底何時使用分支限界而何時使用回溯呢?

回溯法和分支限界法的一些差別:

   方法對解空間樹的搜尋方式       存儲結點的常用資料結構      結點存儲特性常用應用

  回溯法深度優先搜尋堆棧活結點的所有可行子結點被周遊後才被從棧中彈出找出滿足限制條件的所有解

  分支限界法廣度優先或最小消耗優先搜尋隊列、優先隊列每個結點隻有一次成為活結點的機會找出滿足限制條件的一個解或特定意義下的最優解

五大常用算法之五:貪心算法

一、基本概念:

     所謂貪心算法是指,在對問題求解時,總是做出在目前看來是最好的選擇。也就是說,不從整體最優上加以考慮,他所做出的僅是在某種意義上的局部最優解。

     貪心算法沒有固定的算法架構,算法設計的關鍵是貪心政策的選擇。必須注意的是,貪心算法不是對所有問題都能得到整體最優解,選擇的貪心政策必須具備無後效性,即某個狀态以後的過程不會影響以前的狀态,隻與目前狀态有關。

    是以對所采用的貪心政策一定要仔細分析其是否滿足無後效性。

二、貪心算法的基本思路:

    1.建立數學模型來描述問題。

    2.把求解的問題分成若幹個子問題。

    3.對每一子問題求解,得到子問題的局部最優解。

    4.把子問題的解局部最優解合成原來解問題的一個解。

三、貪心算法适用的問題

      貪心政策适用的前提是:局部最優政策能導緻産生全局最優解。

    實際上,貪心算法适用的情況很少。一般,對一個問題分析是否适用于貪心算法,可以先選擇該問題下的幾個實際資料進行分析,就可做出判斷。

四、貪心算法的實作架構

    從問題的某一初始解出發;

    while (能朝給定總目标前進一步)

    {  

          利用可行的決策,求出可行解的一個解元素;

    }

    由所有解元素組合成問題的一個可行解;

五、貪心政策的選擇

     因為用貪心算法隻能通過解局部最優解的政策來達到全局最優解,是以,一定要注意判斷問題是否适合采用貪心算法政策,找到的解是否一定是問題的最優解。

繼續閱讀