00 前言
branch and cut其實還是和branch and bound脫離不了干系的。所以,在開始本節的學習之前,請大家還是要務必掌握branch and bound算法的原理。
01 應用背景
Branch and cut is a method of combinatorial optimization for solving integer linear programs (ILPs), that is, linear programming (LP) problems where some or all the unknowns are restricted to integer values. Branch and cut involves running a branch and bound algorithm and using cutting planes to tighten the linear programming relaxations. Note that if cuts are only used to tighten the initial LP relaxation, the algorithm is called cut and branch.[1]
02 總體描述
前面說過,branch and cut其實還是和branch and bound脫離不了干系。其實是有很大干系的。在應用branch and bound求解整數規划問題的時候,如下圖(好好復習一下該過程):
假如,我們現在求一個整數規划最大化問題,在分支定界過程中,求解整數規划模型的LP松弛模型得到的非整數解作為上界,而此前找到的整數解作為下界。 如果出現某個節點upper bound低於現有的lower bound,則可以剪掉該節點。否則,如果不是整數解繼續分支。
此外,在求解整數規划模型的LP松弛時,If cutting planes are used to tighten LP relaxations。那么這時候的branch and bound就變成了branch and cut。
那么,什么是cutting planes呢?如下圖:
紅色部分是整數規划的可行解空間。
藍色部分是整數規划的LP松弛可行解空間。
在求解LP松弛時,加入橙色的cut,縮小解空間,同時又不影響整數解的解空間,可使解收斂得更快。
這就是branch and cut的過程了。比branch and bound高明之處就在於多了一個cutting planes,可能使branch and bound的效率變得更高。
03 舉個例子
為了讓大家更好了解到branch and cut的精髓,必須得舉一個簡單的例子。對於同一個問題:
branch and cut(左)和branch and bound(右)求解過程如下:
可以看到,兩者的不同之處就在子問題P2的處理上。
-
對於branch and bound來說,求解線性松弛得到的Z = -29.5 < Z = -28。可知該支是可能隱含有更優解的,於是二話不說分支。無奈,分了兩支以后發現居然沒更優解,這種付出了卻沒有回報的感覺就像是受到了欺騙一樣。
-
對於branch and cut來說,在求解線性松弛得到的Z = -29.5 < Z = -28時,並沒有被興奮沖昏頭腦,它嘗試着在線性松弛的解空間上砍下一塊,但又不能影響到整數解的解空間范圍。琢磨半天終於找到一塊能砍的,於是Add cut: 2x1 + x2 <= 7。砍下來以后,形成新的子問題P3,趕緊看看P3的最優解是多少。在P3中,Z=-27.8 > -28,握草,這一支果然不可取。
從上面的算法過程我們可以看到,求解同一個問題,branch and cut只用了3步,而branch and bound卻用了4步。
There are many methods to solve the mixed-integer linear programming. Gomory Cutting Planes is fast, but unreliable. Branch and Bound is reliable but slow. The Branch and cut combine the advantages from these two methods and improve the defects. It has proven to be a very successful approach for solving a wide variety of integer programming problems. We can solve the MILP by taking some cutting planes before apply whole system to the branch and bound, Branch and cut is not only reliable, but faster than branch and bound alone. Finally, we understand that using branch and cut is more efficient than using branch and bound.[2]
04 算法過程
關於branch and cut的過程,可以總結如下:[1]
相比branch and bound,其多了一個Cutting Planes的過程,先用Cutting Planes tighten LP relaxations,然后求解LP relaxations再判斷是否有分支的必要。
其偽代碼如下:
// ILP branch and cut solution pseudocode, assuming objective is to be maximized
ILP_solution branch_and_cut_ILP(IntegerLinearProgram initial_problem) {
queue active_list; // L, above
active_list.enqueue(initial_problem); // step 1
// step 2
ILP_solution optimal_solution; // this will hold x* above
double best_objective = -std::numeric_limits<double>::infinity; // will hold v* above
while (!active_list.empty()) { // step 3 above
LinearProgram& curr_prob = active_list.dequeue(); // step 3.1
do { // steps 3.2-3.7
RelaxedLinearProgram& relaxed_prob = LP_relax(curr_prob); // step 3.2
LP_solution curr_relaxed_soln = LP_solve(relaxed_prob); // this is x above
bool cutting_planes_found = false;
if (!curr_relaxed_soln.is_feasible()) { // step 3.3
continue; // try another solution; continues at step 3
}
double current_objective_value = curr_relaxed_soln.value(); // v above
if (current_objective_value <= best_objective) { // step 3.4
continue; // try another solution; continues at step 3
}
if (curr_relaxed_soln.is_integer()) { // step 3.5
best_objective = current_objective_value;
optimal_solution = cast_as_ILP_solution(curr_relaxed_soln);
continue; // continues at step 3
}
// current relaxed solution isn't integral
if (hunting_for_cutting_planes) { // step 3.6
violated_cutting_planes = search_for_violated_cutting_planes(curr_relaxed_soln);
if (!violated_cutting_planes.empty()) { // step 3.6
cutting_planes_found = true; // will continue at step 3.2
for (auto&& cutting_plane : violated_cutting_planes) {
active_list.enqueue(LP_relax(curr_prob, cutting_plane));
}
continue; // continues at step 3.2
}
}
// step 3.7: either violated cutting planes not found, or we weren't looking for them
auto&& branched_problems = branch_partition(curr_prob);
for (auto&& branch : branched_problems) {
active_list.enqueue(branch);
}
continue; // continues at step 3
} while (hunting_for_cutting_planes /* parameter of the algorithm; see 3.6 */
&& cutting_planes_found);
// end step 3.2 do-while loop
} // end step 3 while loop
return optimal_solution; // step 4
}
相關代碼
關於branch and cut 求解TSP問題的代碼,請關注公眾號【程序猿聲】,在后台回復【bctsp】不包括【】即可獲取。