BranchandBound BFS 1 1 1 2 3 4
Branch-and-Bound
BFS 演算法走訪樹的範例 放入 1 1 取出 1 2 3 4 取出 2 3 4 5 6 7 取出 3 4 5 6 7 8 9 8 9 1 2 1 3 1 4 取出 5 取出 6 取出 7 取出 8~11 取出 12 取出 13~16 6 1 0 1 4 1 5 1 0 1 1 1 5 1 6 1 0 1 1 1 2 1 6 9 1 0 1 1 1 2 1 3 1 4 1 2 1 4
branch-and-bound 以 BFS 走訪法產生的樹 profit weight bound 目前最佳值 maxprofit = 0 40 40 70 90 70 70 70 90 90 10 90 90 90
Breadth-first branch-and-bound Algorithm void breadth_first_branch_and_bound ( state_space_tree T , number& best ) { queue_of_node Q ; //宣告一個佇列用來擺放欲走訪的節點 node u , v ; initialize ( Q ) ; //啟始時將佇列清空 v = root of T ; enqueue ( Q , v ) ; //走訪 root 節點,並將它加入佇列尾端 best = value ( v ) ; //初始化目前最佳值 while ( ! empty ( Q ) ) { dequeue ( Q , v ) ; //從佇列前端取出一個節點 v for ( each child u of v ) { //走訪 v 的每個子節點 u if ( value ( u ) is better than best ) best = value ( u ) ; //若找到更好的結果就更新目前最佳值 if ( bound( u ) is better than best ) enqueue( Q , u ) ; //若一個節點是 promising 就把它加入佇列 } //尾端,以待下一層走訪時使用 } } 11
Algorithm (續) float bound ( node u ) //回傳值就是 u 的 bound 值,呼叫者再把該值和目前最 { index j , k ; //佳值進行比較,以決定 u 是否為 promising int totweight ; float result ; if ( u. weight >= W ) return 0 ; //背包爆掉則傳回 bound = 0 else { result = u. profit ; //先把 bound 值初設為到 u 節點為止的總共獲益值 j = u. level + 1 ; totweight = u. weight ; while ( j <= n && totweight + w[j] <= W ) { totweight = totweight + w[j] ; //盡可能多拿取一些物品 result = result + p[j] ; //直到物品都拿光或是背包 j ++ ; //爆掉為止 } k=j; if ( k <= n ) result = result + (W-totweight)*(p[k]/w[k]) ; //加上第 k 個 return result ; //物品的部份利益 } 下一頁 15 }
使用 Branch-and-bound 修剪法之最佳優先搜尋 profit weight bound 目前最佳值 maxprofit = 0 40 40 70 70 70 90 16 總共走訪了 11個節點, 最佳值=90 90 90 上一頁 下一頁
Best-first Branch-and-bound Algorithm void best_first_branch_and_bound ( state_space_tree T , number& best ) { priority_queue_of_node PQ ; //使用優先權佇列來存放欲走訪的節點 node u , v ; initialize ( PQ ) ; v = root of T ; //先走訪根節點,並初設目前最佳值 best = value ( v ) ; insert ( PQ , v ) ; //將根節點插入優先權佇列中 while ( ! empty ( PQ ) ) { //只要優先權佇列中還有未走訪節點就繼續做 remove ( PQ , v ) ; //取出佇列中優先權 (即 bound) 最高的節點 if ( bound ( v ) is better than best ) //若它仍然還是 promising 則往 for ( each child u of v ) { //下層走訪它的每個子節點 if ( value ( u ) is better than best ) best = value ( u ) ; //更新最佳值 if ( bound ( u ) is better than best ) insert ( PQ , u ) ; //若 u 為 promising,則將 } // 它插入佇列中 } } 17
Algorithm (續) void knapsack 3 ( int n , const int p[] , const int w[] , int W , int& maxprofit ) { priority_queue_of_node PQ ; node u , v ; initialize ( PQ ) ; v. level = 0 ; v. profit = 0 ; v. weight = 0 ; maxprofit = 0 ; v. bound = bound ( v ) ; //走訪根節點,並將它加入優先權佇列中 insert ( PQ , v ) ; while ( ! empty ( PQ , v ) ) { remove ( PQ , v ) ; //將 bound 值最高的節點取出來 if ( v. bound > maxprofit ) { //如果它仍然是 promising 就往下層展開 u. level = v. level + 1 ; u. weight = v. weight + w[ u. level ] ; u. profit = v. profit + p[ u. level ] ; //先走訪左子節點 if ( u. weight <= W && u. profit > maxprofit ) maxprofit = u. profit ; //更新最佳值 19
Algorithm (續) u. bound = bound ( u ) ; if ( u. bound > maxprofit ) insert ( PQ , u ) ; u. weight = v. weight ; u. profit = v. profit ; u. bound = bound ( u ) ; if ( u. bound > maxprofit ) insert ( PQ , u ) ; //若左子節點 promising,則加 //入優先權佇列中 //走訪右子節點 //若右子節點 promising,則加 //入優先權佇列中 } } } bound 函式同 Algorithm 6. 1,請自行參考。 20
優先權佇列變化圖 0 40 40 70 70 (0, 0 )115 (1, 2) (1, 1 (1, 2 )115 )82 (2, 1) (2, 1 (2, 2 (1, 2 )115 )98 )82 (2, 2) (2, 2 (1, 2 (3, 2 )98 )82 )80 (3, 3) (3, 3 (1, 2 (3, 2 ) 98 ) 82 ) 80 70 70 90 90 21 (0, 0) (1, 2) : 82 < 90 (1, 2 (3, 2 nonpromising ) 82 ) 80 (3, 2) : 80 < 90 (3, 2 nonpromising ) 80
計算狀態樹中每個節點的bound值(續) 計算節點 [1, 2] 的 bound 值: V 1 V 2 minimum ( 7 , 8 , 7 ) V 3 minimum ( 4 , 7 , 16 ) V 4 minimum ( 11 , 9 , 2 ) V 5 minimum ( 18 , 7 , 17 , 4 ) 14 (已固定不能變動) =7 =4 =2 =4 bound 值 = 14+7+4+2+4 = 31 25
狀態樹與 Bound 值 最佳解 = 無限大 31 31 31 37 5, 4 27 31 31 31 30 30
Algorithm (續) void travel 2 ( int n , const number W[][] , ordered_set& opttour , number& minlength ) { priority_queue_of_node PQ ; node u , v ; initialize ( PQ ) ; v. level = 0 ; v. path = [1] ; // 令頂點 1 為起點 (即根節點) v. bound = bound( v ) ; //計算根節點的 bound 值 minlength = 無限大 ; //初始化最佳值 insert ( PQ , v ) ; while ( ! empty ( PQ ) ) { remove ( PQ , v ) ; //取出下一個要展開的節點 if ( v. bound < minlength ) { //如果它仍然是 promising u. level = v. level + 1 ; //開始計算它所有下一層節點 for ( all i such that 2 <= i <= n && i is not in v. path ) { u. path = v. path ; put i at the end of u. path ; //將 u 的路徑寫入 30
Algorithm (續) if ( u. level == n - 2 ) { //如果 u 在倒數兩層就直接處理 put index of only vertex not in u. path at the end of u. path ; //將最後一頂點加入路徑 put 1 at the end of u. path ; //把頂點 1 加入 if ( length ( u ) < minlength ) { minlength = length ( u ) ; //更新最佳值 opttour = u. path ; //更新最佳路徑 } } else { //還不到旅程的終點 u. bound = bound ( u ) ; //計算 bound 值 if ( u. bound < minlength ) //若 promising insert ( PQ , u ) ; //放入優先權佇列中 } } } 31
- Slides: 31