對比回溯法
- 回溯法的求解目標是找出解空間中滿足約束條件的所有解,想必之下,分支限界法的求解目標則是找出滿足約束條件的一個解,或是滿足約束條件的解中找出使某一目標函數值達到極大或極小的解,即在某種意義下的最優解。
- 另外還有一個非常大的不同點就是,回溯法以深度優先的方式搜尋解空間,而分支界限法則以廣度優先的方式或以最小耗費優先的方式搜尋解空間。
分支限界法的搜尋策略
在當前節點(擴充節點)處,先產生其所有的兒子節點(分支),然後再從當前的活節點(當前節點的子節點)表中選擇下一個擴充節點。為了有效地選擇下一個擴充節點,加速搜尋的進程,在每一個活節點處,計算一個函數值(限界),並根據函數值,從當前活節點表中選擇一個最有利的節點作為擴充節點,使搜尋朝著解空間上有最優解的分支推進,以便儘快地找出一個最優解。分支限界法解決了大量離散最佳化的問題。
選擇方法
1.隊列式(FIFO)分支限界法
隊列式分支限界法將活節點表組織成一個隊列,並將隊列的先進先出原則選取下一個節點為當前擴充節點。
2.優先隊列式分支限界法
優先隊列式分支限界法將活節點表組織成一個優先隊列,並將優先隊列中規定的節點優先順序選取優先順序最高的下一個節點成為當前擴充節點。如果選擇這種選擇方式,往往將資料排成最大堆或者最小堆來實現。
例子:裝載問題
有一批共n個集裝箱要裝上2艘載重量分別為c1,c2的輪船,其中集裝箱i的重量為wi,且要求確定是否有一個合理的裝載方案可將這n個集裝箱裝上這2艘輪船。
可證明,採用如下策略可以得到一個最優裝載方案:先儘可能的將第一艘船裝滿,其次將剩餘的集裝箱裝到第二艘船上。
代碼如下:
1 //分支限界法解裝載問題 2 3 //子函數,將當前活節點排入佇列 4 template<class Type> 5 void EnQueue(Queue<Type> &Q, Type wt, Type &bestw, int i, int n) 6 { 7 if(i == n) //可行葉結點 8 { 9 if(wt>bestw) bestw = wt ;10 }11 else Q.Add(wt) ; //非葉結點12 }13 14 //裝載問題先盡量將第一艘船裝滿15 //隊列式分支限界法,返回最優載重量16 template<class Type>17 Type MaxLoading(Type w[],Type c,int n)18 {19 //初始化資料20 Queue<Type> Q; //儲存活節點的隊列21 Q.Add(-1); //-1的標誌是標識分層22 int i=1; //i表示當前擴充節點所在的層數23 Type Ew=0; //Ew表示當前擴充節點的重量24 Type bestw=0; //bestw表示當前最優載重量25 26 //搜尋子集空間樹27 while(true)28 {29 if(Ew+w[i]<=c) //檢查左兒子30 EnQueue(Q,Ew+w[i],bestw,i,n); //將左兒子添加到隊列31 32 //將右兒子添加到隊列 即表示不將當前貨物裝載在第一艘船33 EnQueue(Q,Ew,bestw,i,n);34 Q.Delete(Ew); //取下一個節點為擴充節點並將重量儲存在Ew35 if(Ew==-1) //檢查是否到了同層結束36 {37 if(Q.IsEmpty()) return bestw; //遍曆完畢,返回最優值38 Q.Add(-1); //添加分層標誌39 Q.Delete(Ew); //刪除分層標誌,進入下一層40 i++;41 }42 }43 }
演算法MaxLoading的計算時間和空間複雜度為O(2^n).
上述演算法可以改進,設r為剩餘集裝箱的重量,當Ew+r<=bestw的時候,可以將右子樹剪去。因為最優值不可能出現在下面了。
改進代碼如下:
分支限界法解裝載問題的改進
1 //分支限界法解裝載問題 2 3 //裝載問題先盡量將第一艘船裝滿 4 //隊列式分支限界法,返回最優載重量 5 template<class Type> 6 Type MaxLoading(Type w[],Type c,int n) 7 { 8 //初始化資料 9 Queue<Type> Q; //儲存活節點的隊列10 Q.Add(-1); //-1的標誌是標識分層11 int i=1; //i表示當前擴充節點所在的層數12 Type Ew=0; //Ew表示當前擴充節點的重量13 Type bestw=0; //bestw表示當前最優載重量14 15 //搜尋子集空間樹16 while(true)17 {18 //檢查左兒子19 Type wt=Ew+w[i]; //wt為左兒子節點的重量20 if(wt<=c) //若裝載之後不超過船體可承受範圍21 if(wt>bestw) //更新最優裝載重量22 {23 bestw=wt;24 if(i<n) Q.Add(wt); //將左兒子添加到隊列25 }26 27 //將右兒子添加到隊列28 if(Ew+r>bestw&&i<n)29 Q.Add(Ew);30 Q.Delete(Ew); //取下一個節點為擴充節點並將重量儲存在Ew31 if(Ew==-1) //檢查是否到了同層結束32 {33 if(Q.IsEmpty()) return bestw; //遍曆完畢,返回最優值34 Q.Add(-1); //添加分層標誌35 Q.Delete(Ew); //刪除分層標誌,進入下一層36 i++;37 r-=w[i]; //剩餘集裝箱重量38 }39 }40 }
用處
分支限界法解決了大量離散最佳化問題。
參考資料電腦演算法設計與分析/王曉東編著。-3版。-北京:電子工業出版社,2007.5