對比回溯法
- 回溯法的求解目標是找出解空間中滿足約束條件的所有解,想必之下,分支限界法的求解目標則是找出滿足約束條件的一個解,或是滿足約束條件的解中找出使某一目標函數值達到極大或極小的解,即在某種意義下的最優解。
- 另外還有一個非常大的不同點就是,回溯法以深度優先的方式搜索解空間,而分支界限法則以廣度優先的方式或以最小耗費優先的方式搜索解空間。
分支限界法的搜索策略
在當前節點(擴展節點)處,先生成其所有的兒子節點(分支),然后再從當前的活節點(當前節點的子節點)表中選擇下一個擴展節點。為了有效地選擇下一個擴展節點,加速搜索的進程,在每一個活節點處,計算一個函數值(限界),並根據函數值,從當前活節點表中選擇一個最有利的節點作為擴展節點,使搜索朝着解空間上有最優解的分支推進,以便盡快地找出一個最優解。分支限界法解決了大量離散最優化的問題。
選擇方法
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); //取下一個節點為擴展節點並將重量保存在Ew 35 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); //取下一個節點為擴展節點並將重量保存在Ew 31 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