五大常用算法之一:分治算法
基本概念:
把一個復雜的問題分成兩個或更多的相同的或相似的子問題。再把子問題分成更小的子問題。直到最后子問題可以簡單的解決。
分成的子問題與原問題形式相同,并且互相獨立,遞歸的解決這些子問題。然后將子問題的解合并得到原問題的較小模式。這就為使用遞歸技術提供了方便。分治與遞歸像一對孿生兄弟,經常同時應用在算法設計之中。
分治法適用范圍情況:
1)該問題規模縮小到一定程度就可以輕易解決
2)該問題可以分解為若干個規模較小相同問題,即該問題具有最優子結構性質。
3)利用該問題分解出來的子問題的解可以合并為該問題的解。
4)該問題所分解出的各個子問題是相互獨立的。不包含公共子問題。
如果第三條和第四條不滿足,分治法要做許多不必要的方法,需要使用動態規劃法較好。
分治法的基本步驟:
step1:分解問題:分成規模較小,相互獨立,與原問題形式相同的子問題。
step2:若子問題規模較小容易解決直接解決,否則遞歸的解各個子問題。
一般設計模式:
Divider-and-Conquer(P)//表示問題的規模
1.if |p|<n ?//當問題小于一個閾值,就可以直接解決了
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)
分治法的復雜性分析
總結
1.一定是要找到最小規模的解決辦法
2.找到求解的遞歸函數式后(各種規模或因子),設計遞歸程序即可。
五大常用算法之二:動態規劃算法
基本概念
每次決策依賴于當前狀態,又隨即引起狀態的轉移。一個決策的序列就是就是在變化的狀態中產生出來的。所以,這種多階段最優化決策解決問題的過程叫動態規劃。
在思想上與分治法類似,將待求解問題分為若干個子問題,按順序求解子階段,前一子問題的解為后一子問題的求解提供了有用的休息。子啊求解任一子問題時,可以列出各種可能的局部解,通過決策保留那些有可能達到最優的局部解,丟其舍棄其它局部解。依次解決各子問題。最后一個子問題就是初始問題的解。
動態規劃解決問題多數有重疊子問題這個特點,為減少重復計算,對每一個子問題只解一次,將其不同階段的不同狀態保存在一個二維數組中。與分治法最大的差異是:適合于動態規劃法求解的問題,經分解后問題往往不是相互獨立的
適用的情況
1)最優化原理:如果問題的最優解所包含的子問題也是最優的,也就稱該問題具有最優子結構,即滿足最優化原理。
2)無后效性:某個階段一旦確定,就不受這個狀態以后決策的影響。也就是說,某狀態以后的過程不會影響以前的狀態,只與當前的狀態有關。
動態規劃算法最大的優勢,就是解決子問題之間重疊的問題。
求解基本步驟
處理的是一個多階段決策問題,一般是由初始狀態開始,對中間階段決策的選擇,達到結束時,這些決策形成了一個決策序列,同時確定了完成整個過程的一條活動路線。
(1)劃分階段:按照問題的時間或空間特征,把問題分為若干個階段。在劃分階段時,注意劃分后的階段一定是要有序的或者是可排序的。否則問題就無法求解。
(2)確定狀態和狀態變量:將問題發展到各個階段時所處于的客觀情況用不同的狀態表示出來,當然狀態的選擇要滿足無后效性。
(3)確定決策并寫出狀態轉移方程:因為決策和狀態轉移有著天然的聯系,狀態轉移就是根據上一階段的狀態和決策來導出本階段的狀態。所以如果確定了決策,狀態轉移方程也就可以寫出。但一般是根據前后的兩個階段的狀態之間的關系來確定決策方法和狀態轉移方程的。
(4)尋找邊界條件:狀態轉移方程是一個遞推式,需要一個遞推的終止條件或邊界條件。
五大常用算法之三:回溯法
在包含問題的所有解的解空間樹中,按照深度優先搜索的策略,從根結點出發深度探索解空間樹。當探索到某一結點時,要先判斷該結點是否包含問題的解,如果包含,就從該結點出發繼續探索下去,如果該結點不包含問題的解,則逐層向其祖先結點回溯。(其實回溯法就是對隱式圖的深度優先搜索算法)。
若用回溯法求問題的所有解時,要回溯到根,且根結點的所有可行的子樹都要已被搜索遍才結束。
而若使用回溯法求任一個解時,只要搜索到問題的一個解就可以結束。
回溯法解題的一般步驟
(1)針對所有問題。確定問題的解空間。
首先應明確定義問題的解空間,問題的解空間應至少包含問題的一個(最優)解。
(2)確定結點的擴展搜索規則
以深度優先方式搜索解空間,并在搜索過程中用剪枝函數避免無效搜索。
算法框架
(1)問題框架
設問題的解是一個n維向量(a1,a2。。an),約束條件是ai之間滿足某種條件,記為f(ai)
非遞歸回溯框架
int a[n],i;
初始化數組a[];
i=1;
while(i>0有路可走,并且未達目標)
{
? ? ? ? if(i>n){
? ? ? ? ? ?搜索到一個解,輸出;
}else{
a[i]第一個可能得值
while(a[i]在不滿足約束條件且在搜索空間內){
? ? ? ? ? a[i]下一個可能的值
}
}
}
遞歸的算法框架
一般情況下使用遞歸函數來實現回溯法比較簡單,其中i為搜索的深度。
int a[n];
try(int i){
if(i>n)
輸出結果;
else{
? ? ? ? for(j=下界;j<=上界;j=j+1)//枚舉i所有可能得路徑
{
? ? ? ? ?if(fun(j)){
? ? ? ? ? a[i]=j;
}
}
}
}
五大常用算法之四:分支限界法
分支限界法與回溯法類似,但是回溯法是求解目標中所有滿足約束條件的解,而分支限界法是找出滿足約束條件的一個解,最優。
(1)分支搜索算法
會有幾種不同的分支搜索方式。
FIFO搜索,LIFO搜索,優先隊列式算法搜索。
分支限界法的一般過程
在每一活結點出,計算一個函數值(限界),并根據這些已計算出的函數值,從當前活結點表中選擇一個最有利的節點作為擴展節點。
回溯法和分支限界法的一些區別
回溯法深度優先搜索堆棧活結點的所有可行子節點被遍歷后,才被從棧中彈出找出滿足約束條件的所有解。
分支限界法廣度優先或最小消耗優先搜索隊列,優先隊列每個結點只有一次成為活結點的機會找出滿足約束條件的一個解或特定意義下的最優解。
五大常用算法之貪心算法
基本概念
所謂貪心算法是指,在對問題求解時,總是做出在當前看來是最好的選擇,不從整體最優考慮,做出的只是某種意義上的局部最優解。
它不具有固定的算法框架,算法設計的關鍵是貪心策略的選擇,貪心算法不是對所有問題都能得到整體最優解。選擇的貪心策略必須具備無后效性,即某個狀態以后的狀態不會影響到以前的狀態。所以對采用的貪心策略一定要仔細分析是否滿足無后效性。
建立數學模型來描述問題。
把求解的問題分成若干個子問題。
對每一子問題求解,得到子問題的局部最優解。
把子問題的解局部最優解合成原來解問題的一個解。
貪心策略使用的前提是,局部最優策略能導致產生全局最優解。
貪心算法的實現框架
從某一問題的初始解出發:
while(朝給定總目標前進一步){
利用可行的決策,求出可行解的一個解元素;
}
由所有解元素組合成問題的一個可行解。
例子
[背包問題]有一個背包,背包容量是M=150.有7個物品,物品可以分割成任意大小。
要求盡可能讓裝入背包中的物品總價值最大,但不能超過容量。
A B C D E F G
重量 35 30 60 50 40 10 25
價值 10 40 30 50 35 40 30
分析:目標函數:E pi最大
約束條件:E wi<=M(M=150)
(1)根據貪心的策略,每次挑選價值最大的物品裝入背包,得到的結果是否最優
(2)每次挑選所占重量最小的物品裝入是否能的到最優
每次挑選單位重量價值最大的物品,稱為解本題的策略。雖然貪心算法經過證明之后,很高效,并且簡單易行,但是它必須經過證明才能真正運用到題目的算法中去。
一般來說,貪心算法的證明圍繞著:整個問題的最優解一定由貪心策略中存在的子問題最優解得來的。對于例題中的幾種貪心策略,都是無法成立的。
五大常用算法之三:回溯法
回溯算法實際上一個類似枚舉的搜索嘗試過程,主要是在搜索嘗試過程中尋找問題的解,當發現已不再滿足求解條件時,就回溯返回,查找別的路徑。
從根節點出發深度搜索解空間樹,當探索到某一點時,先判斷該節點是否包含問題的解,如果包含,就從該結點出發繼續探索下去,如果該結點不包含問題的解