0-1整數規划與隱枚舉法-感受剪枝的魅力
整數規划是線性規划的特殊情況,即當約束條件是變量為整數時,線性規划就變成了整數規划。若要求所有變量都為整數,即為純整數規划;若允許存在一部分變量不一定為整數,則稱為混合整數規划。而本文要討論的0-1整數規划則是純整數規划的特殊情況,即所有變量要么等於0,要么等於1,故這種變量又成為邏輯變量。
0-1整數規划在生活中還是很常見的,通常可以總結為“是”“否”問題。例如,有n個產品銷地x1,...,xn可供選擇,為使得利潤最大,那么每一個銷地都面臨是否選擇的問題,通常還會有一些限制條件,由於銷地xi與銷地xj距離較近,所以規定若選擇xi就不能選擇xj等。那么如何求解0-1規划問題?最朴素的方法是枚舉,即將所有銷地是否被選擇的情況都考慮,那么就是從{0, ... ,0}枚舉到{1, ... ,1},需要2的n次方的枚舉次數。顯然,當n較大時,這種方式的效率就非常低。本文要介紹的隱枚舉法就可以提高求解出最優解的效率。
所謂隱枚舉法,從字面上理解,就是隱去一些不需要枚舉的情況,下面從一個例子出發,來給出隱枚舉法的步驟。
【例】求解下列規划問題
max z = 8*x1 + 2*x2 - 4*x3 - 7*x4 - 5*x5;
s.t.{
3*x1 + 3*x2 + x3 + 2*x4 + 3*x5 <= 4
5*x1 + 3*x2 - 2*x3 - x4 + x5 <= 4
xi = 0或1,i = 1, ..., 5
}
1. 預處理
首先需要對原問題進行預處理,至於為什么后文將會解釋。預處理的步驟如下:
1) 將目標函數統一為求最小值,即"min", 同時將約束條件都化為">="。
- 若原約束條件為"<=",則不等式左右同乘-1;
- 若原約束條件為"Ai * X = bi",則化為"Ai * X >= bi" 和 "-Ai * X >= -bi",其中Ai為系數行向量,X為變量列向量。
min z' = -8*x1 - 2*x2 + 4*x3 + 7*x4 + 5*x5;
s.t.{
-3*x1 - 3*x2 - x3 - 2*x4 - 3*x5 >= 4
-5*x1 - 3*x2 + 2*x3 + x4 - x5 >= 4
xi = 0或1,i = 1, ..., 5
}
2) 將目標函數中系數為負的變量xi化為系數為正的變量xi',其中 xi = 1 - xi’ (若xi = 0 則 xi' = 1; 若xi = 1則xi' = 0)。
故針對本問題,在目標函數中x1和x2前的系數為負,故令x1 = 1 -x1', x2 = 1 - x2',代入1)中化簡得
min z' = 8*x1' + 2*x2' + 4*x3 + 7*x4 + 5*x5 - 10;
s.t.{
3*x1' + 3*x2' - x3 - 2*x4 - 3*x5 >= 2
5*x1' + 3*x2' + 2*x3 + x4 - x5 >= 4
xi或xi' = 0或1,i = 1, ..., 5
}
3) 重新排列變量在目標函數和約束條件的先后順序,使其在目標函數中的系數遞增。
min z' = 2*x2' + 4*x3 + 5*x5 + 7*x4 + 8*x1' - 10;
s.t.{
3*x2' - x3 - 3*x5 - 2*x4 + 3*x1' >= 2 (a)
3*x2' + 2*x3 - x5 + x4 + 5*x1' >= 4 (b)
xi或xi' = 0或1,i = 1, ..., 5
}
2. 隱枚舉
隱枚舉的思想是首先枚舉找到一個可行解,並得到目標函數值z0,之后的枚舉若目標函數值沒有z0優,那么就一定不是最優解。
現在說明預處理的作用:
預處理使得目標函數是求最小值,變量的系數都為正且由小到大排列,所以有如下規律:
- 從xi = 0開始枚舉是使目標函數最優的,此時得到的函數值也就是最優解的下界;
- 只要按照目標函數中變量的順序枚舉也就是二進制數位從小到大(0...0到1...1)就能盡量較早的枚舉出使得目標函數取最小值(最優值)的可行解z0。
- 若解形如0..0xj...x1的目標函數z1取值大於已得到的可行解,那么只要是以xj...x1結尾和只將xj...x1中某些位由0變為1的解的目標函數取值一定大於z1,當然也就大於z0,故一定不會是最優解,可以直接剪枝,即不考慮上述兩種情況,直接按照二進制數碼順序枚舉其他形式。
隱枚舉步驟如下:
(1) 先忽略除" xi或xi' = 0或1 "以外的約束條件,從xi = 0也就是0...0開始枚舉。
(2) 計算出枚舉出的目標函數值。
- 若小於已有可行解的函數值,或者還無可行解,則執行(3);
- 若大於已有可行解的函數值,剪枝,再進行枚舉。
(3) 檢查枚舉的解是否滿足除去的約束條件。(只要檢查出一個約束條件不滿足就無需再檢查)
- 若不滿足,則此時的枚舉值不是可行解,繼續枚舉;
- 若滿足,則更新可行解和目標函數值z0。可行解0..0xj...x1(前面'0'的個數可能為0),那么只要是以xj...x1結尾和只將xj...x1中某些位由0變為1的解的目標函數取值一定大於z0,剪枝,再進行枚舉。
對於本問題,從xi = 0 (i = 1到5)開始枚舉,得到z' = -10,所以-10便是最優解的下界(所以10便是原問題的上界)。枚舉過程列表如下('-'代表沒有判斷):
x1' x4 x5 x3 x2' | z' | 是否(Y/N)滿足約束條件 (a) (b) |
是否(Y/N)為可行解 |
0 0 0 0 0 0 0 0 0 1 0 0 0 1 0 0 0 0 1 1 |
-10 -8 -6 -4 |
N - Y N N - Y Y |
N N N Y 剪枝 |
0 0 1 0 0 0 0 1 0 1 0 0 1 1 0 0 1 0 0 0 0 1 1 0 0 |
-5 -3 -1 -3 2 |
N - 函數值-3大於已知可行解的函數值-4,一定不會是最優解 -1 > -4 -3 > -4 2 > -4 |
N 無需判斷約束條件,該分支也無需再枚舉,即剪枝 同上 同上 同上 |
由表可以看出,我們在第4次枚舉得到了一個較優的可行解,其目標函數值z0 = -4,之后的枚舉要么是不滿足約束條件,要么是函數值大於-4,剪枝。最后我們只枚舉了9次就完成了整個過程(比直接枚舉的2^5 = 32次快了很多),得到最優解為{0,0,0,1,1},min z' = -4,將其還原成原問題,最優解為{1, 0, 1, 0, 0},max z = 4.
總結:
在解決很多問題的時候,枚舉(搜索)似乎是一種直接了當的方式。但是,當解空間較大時,枚舉的效率可能就很低,無法達到目的。此時,不妨想想是否在枚舉過程中有一些解可以在枚舉之前就判斷它一定不滿足要求,直接不考慮它們(剪枝),這樣就可以縮小解空間,提高效率。