ortools系列:分配問題
組合優化中最重要的問題之一是分配問題,其中一組工作者必須執行一組任務。對於每個工人和任務,該工人執行任務的成本是固定的。問題是給每個工人分配不同的任務,以使總成本最小化。分配問題的一個典型就是工作排班,對於工廠這種三班倒或者公交司機兩班倒,該怎麼排班,才能照顧到每個人的工作和休息時間。
這裡有一個例子,假設一家計程車公司有四個客戶在等車,四個計程車司機(工人)可以接他們,任務是給每個客戶分配一名司機去接他們。任務的成本是司機接一個特定客戶所花費的時間,問題是,將司機分配給客戶,以最小化總等待時間。
你可以在下面的二部圖中想像這個問題。左邊的節點表示worker(司機)。右邊的節點表示任務(客戶),這些邊表示將工作人員分配給任務的所有可能方法。
成本矩陣
下表給出了司機接客戶的成本(等待時間),稱為成本矩陣。
from ortools.graph import pywrapgraph import time
# 數組為代價矩陣,其中i, j項為工作者i執行任務j的代價。 # 矩陣的行對應4個工作者,列對應4個任務。 def create_data_array(): cost = [[90, 76, 75, 70], [35, 85, 55, 65], [125, 95, 90, 105], [45, 110, 95, 115]] return cost
def main():
# 創建數據 cost = create_data_array() rows = len(cost) cols = len(cost[0])
# 創建分配問題的求解器 # 這裡使用 線性賦值求解器,這是一種針對賦值問題的專用求解器 assignment = pywrapgraph.LinearSumAssignment()
# 添加分配網路的弧的成本,以構建分配問題的網路 # 實用assignment.AddArcWithCost for worker in range(rows): for task in range(cols): if cost[worker][task]: assignment.AddArcWithCost(worker, task, cost[worker][task])
# 求解問題,並列印結果 solve_status = assignment.Solve() if solve_status == assignment.OPTIMAL: print(Total cost = , assignment.OptimalCost()) print() for i in range(0, assignment.NumNodes()): print(Worker %d assigned to task %d. Cost = %d % ( i, assignment.RightMate(i), assignment.AssignmentCost(i))) elif solve_status == assignment.INFEASIBLE: print(No assignment is possible.) elif solve_status == assignment.POSSIBLE_OVERFLOW: print(Some input costs are too large and may cause an integer overflow.)
if __name__ == "__main__": start_time = time.clock() main() print() print("Time =", time.clock() - start_time, "seconds")
# 結果 Total cost = 265
Worker 0 assigned to task 3. Cost = 70 Worker 1 assigned to task 2. Cost = 55 Worker 2 assigned to task 1. Cost = 95 Worker 3 assigned to task 0. Cost = 45
Time = 0.0003038 seconds
我們看這裡使用的求解器,pywrapgraph.LinearSumAssignment(),這裡使用線性賦值求解器,這是一種針對賦值問題的專用求解器,線性賦值求解器只接受整數類型的權重和數值。關於這個問題可以參考Using_a_solver_with_non-integer_data.
pywrapgraph.LinearSumAssignment()
我們的求解結果如下:虛線旁邊的數字是它們的成本。這個任務的總等待時間是虛線的代價之和,即265。
再看圖上圖,在圖論中,對半圖中的一組邊,它匹配左邊的每個節點和右邊的一個節點,稱為完全匹配。
在前面的例子中,我們假設所有的worker都可以執行所有的任務。但情況並非總是如此,工作人員可能由於各種原因無法執行一個或多個任務。但是,我們很容易修改上面的代碼來處理這個問題。
例如,假設worker 0無法執行task 3。要修改程序以考慮到這一點,則進行以下更改:
# 1.將成本矩陣的某個成本設置為NA cost = [[90, 76, 75, NA], [35, 85, 55, 65], [125, 95, 90, 105], [45, 110, 95, 115]]
# 2.在設置弧的時候,跳過成本為NA的弧 for worker in range(0, rows): for task in range(0, cols): if cost[worker][task] != NA: assignment.AddArcWithCost(worker, task, cost[worker][task])
大家看完記得關注點贊.
master蘇.