ortools系列-整數規劃
有一些問題,要求一些但不是全部變數約束為整數,這類問題可以用混合整數規劃(MIP)來解決,混合整數規劃也稱為混合整數線性規劃(MILP)。這裡有幾個例子:
OR-Tools提供了幾種方法求解此類問題:
其中前兩個是一般性的求解器,能解決任何整數規劃問題,最後一個最小成本流求解器用於求解那些能建模成網路流的問題,對於這類特殊問題,最小成本流求解器比一般的整數規劃和約束規劃要快的多。
ortools提供一個混合整數規劃的介面用於調用不同的求解器,ortools內置了Coin-or branch and cut (CBC),當然你也可以使用第三方的求解器,比如SCIP,GLPK,Gurobi。CBC是一個開源的混合整數規劃求解器,用C++開發。
Coin-or branch and cut (CBC)
SCIP
GLPK
Gurobi
我們看下面這個問題:
問題的解空間如下:
我們看ortools是如何解決這個問題的。
from ortools.linear_solver import pywraplp
def main():
# 首先,調用CBC求解器 # 還記得我們在LP問題中調用的求解器嗎, # 是 pywraplp.Solver.GLOP_LINEAR_PROGRAMMING solver = pywraplp.Solver(SolveIntegerProblem, pywraplp.Solver.CBC_MIXED_INTEGER_PROGRAMMING)
# 和LP問題一樣,定義變數 x,y 並指定其定義域 # 這裡需要注意,我們定義的是Int類型的變數 x = solver.IntVar(0.0, solver.infinity(), x) y = solver.IntVar(0.0, solver.infinity(), y)
# 添加約束,方法和LP一樣,通過制定係數來添加約束 # 如果能像cp-sat那麼方便就好了 # x + 7 * y <= 17.5 constraint1 = solver.Constraint(-solver.infinity(), 17.5) constraint1.SetCoefficient(x, 1) constraint1.SetCoefficient(y, 7)
# 添加約束 # x <= 3.5 constraint2 = solver.Constraint(-solver.infinity(), 3.5) constraint2.SetCoefficient(x, 1) constraint2.SetCoefficient(y, 0)
# 定義目標函數 # Maximize x + 10 * y. objective = solver.Objective() objective.SetCoefficient(x, 1) objective.SetCoefficient(y, 10) objective.SetMaximization()
# 求解問題,並列印結果,後面的代碼就很簡單了 result_status = solver.Solve() # The problem has an optimal solution. assert result_status == pywraplp.Solver.OPTIMAL
# The solution looks legit (when using solvers other than # GLOP_LINEAR_PROGRAMMING, verifying the solution is highly recommended!). assert solver.VerifySolution(1e-7, True)
print(Number of variables =, solver.NumVariables()) print(Number of constraints =, solver.NumConstraints())
# The objective value of the solution. print(Optimal objective value = %d % solver.Objective().Value()) print() # The value of each variable in the solution. variable_list = [x, y]
for variable in variable_list: print(%s = %d % (variable.name(), variable.solution_value()))
if __name__ == __main__: main()
# 結果 Number of variables = 2 Number of constraints = 2 Optimal objective value = 23
x = 3 y = 2
通過代碼可以看到,其實和LP問題基本一樣,思路是很清晰,就是囉嗦呢。
前面我們講了CBC解MIP的問題。約束規劃是一種有別於經典優化理論的優化方法。CP的基礎是可行性(尋找問題的可行解),側重於約束和變數,而不是目標函數。對於許多類型的問題,CP可以比MIP求解器更快地找到最優解。
那我們應該如何選擇CP還是MIP呢。
對於一個可行點必須滿足所有約束的標準整數規劃問題,MIP求解器速度更快。在這種情況下,可行集是凸的:對於集合中的任意兩點,連接它們的線段完全位於集合中。
另一方面,對於高度非凸可行集的問題,CP-SAT求解器通常比MIP求解器快。當可行集由許多由or連接的約束定義時,就會出現這樣的問題,or意味著一個點只需要滿足其中一個約束就可以是可行的。
為了提高計算速度,CP-sat求解器和原始CP求解器都對整數進行運算。要解決某些約束具有非整數項的問題,必須首先將這些約束轉換為一個足夠大的整數。下面的示例說明瞭這一點。
我們來看代碼:
from ortools.sat.python import cp_model
# 首先是創建模型 model = cp_model.CpModel()
# 定義變數,指定變數的範圍 # 根據題目可以知道,x,y,z 的最大值肯定不超過50 # var_upper_bound 相當於減少了搜索空間範圍 # 注意,我們這裡定義的是整數變數 var_upper_bound = max(50, 45, 37) x = model.NewIntVar(0, var_upper_bound, x) y = model.NewIntVar(0, var_upper_bound, y) z = model.NewIntVar(0, var_upper_bound, z)
# 添加約束 # 注意到,我們第一個約束條件是 x + 7?2 y + 3?2 z ≤ 25 # 但是 CP-SAT 只能處理整數問題,所以需要對第一個約束處理,使得係數全部是整數 # 方法很簡單,左邊右邊都乘以2就可以了 model.Add(2 * x + 7 * y + 3 * z <= 50) # 添加第二個約束 model.Add(3 * x - 5 * y + 7 * z <= 45) # 添加第三個約束 model.Add(5 * x + 2 * y - 6 * z <= 37)
# 定義目標函數 model.Maximize(2 * x + 2 * y + 3 * z)
# 求解並列印結果 solver = cp_model.CpSolver() status = solver.Solve(model)
if status == cp_model.OPTIMAL: print(Maximum of objective function: %i % solver.ObjectiveValue()) print() print(x value: , solver.Value(x)) print(y value: , solver.Value(y)) print(z value: , solver.Value(z))
# 結果 Maximum of objective function: 35
x value: 7 y value: 3 z value: 5
看起來也不複雜呢,因為我們的例子也不複雜啊,現實生活中的大部分問題都是大規模整數規劃問題,說不定跑半天都沒出結構。
ortools的文檔中還是一個例子,是用 original-cp 來求解上面的問題,和之前一樣的套路,感興趣的同學可以看看。
大家看完記得關注點贊.
master蘇.