python3 分支界限法解決作業分配問題
阿新 • • 發佈:2019-02-14
學號:S201625005 姓名:樑勳 聯絡電話:13126734215 執行環境:MacOS serria 10.12.4 Beta (16E163f) 程式語言:Python3 分析設計: 有n份作業分配給n個人去完成,每人完成一份作業。 假定第i個人完成第j份作業需要花費cij時間,cij>0,1≦i,j≦n。試設計一個分支界限演算法,將n份作業分配給n個人完成,使得總花費時間最少。 這是一個尋找最優解的問題,通常的解決辦法就是窮舉出所有結果,找出最優解。分支界限演算法是為了有效的避免窮舉結果,這是一個廣度優先的搜尋操作 資料結構: Node 類: self.deep = 0 # 標記該節點的深度 self.cost = 0 # 標記到達該節點的總消費 self.father = None # 標記該節點的父節點 self.value = 0 # 本節點的消費值 self.worker = None # 本節點的該任務由第幾位工人完成 Node類用來存放分支樹上的所有有效節點 Worker 類: max = 0 # 上界 通過貪心演算法找出近似值 min = 0 # 下界 由每組的最小值組成 pt_nodes = [] # 存放可擴充套件的節點 FIFO pt_flag = 0 # 標記佇列是否被使用 用於結束演算法 input_file = '' # 輸入檔名 output_file = '' # 輸出檔名 matrix = [] # 存放資料矩陣 行為單個任務 每個工人 完成所要的時間 n = 0 # 資料矩陣的大小 n*n min_leaf_node = None # 消耗最小的節點 function: read_data_from_file 從檔案中讀取資料 get_low_limit 計算下界 get_up_limit 計算上界 branch_limit 執行分支界限演算法 output_result 結果輸出到檔案 演算法思路: 1 將資料用n*n的矩陣來描述 M[i,j]表示第i個任務由第j位工人完成所耗時間 2 使用貪心演算法計算一個近似的最優解作為上界 3 最小值求和法得到最優解的下界 4 針對第一個任務,檢查每人的耗時是否超過上界,是則捨棄,否則建立節點加入佇列 5 開始處理佇列元素,先進先出,如果是子節點,判斷是否是最優節點,否則 檢查累積上下一個任務每人的耗時後是否超過上界,否則繼續建立節點加入佇列,是則捨棄 6 當佇列處理完畢後,層層遍歷最優節點的father節點,輸出結果
程式碼如下:
# 分支演算法執行類 class Worker: max = 0 # 上界 通過貪心演算法找出近似值 min = 0 # 下界 由每組的最小值組成 pt_nodes = [] # 存放可擴充套件的節點 pt_flag = 0 # 標記佇列是否被使用 用於結束演算法 input_file = '' # 輸入檔名 output_file = '' # 輸出檔名 matrix = [] # 存放資料矩陣 行為單個任務 每個工人 完成所要的時間 n = 0 # 資料矩陣的大小 n*n min_leaf_node = None # 消耗最小的節點 # 初始化引數def __init__(self, input_file, output_file): self.input_file = input_file self.output_file = output_file self.read_data_from_file() self.n = len(self.matrix) self.get_low_limit() self.get_up_limit() # print(self.matrix) # print(self.n)# print(self.max) # print(self.min) # 從檔案中讀取資料 初始化資料矩陣 def read_data_from_file(self): with open(self.input_file) as source: for line in source: data_cluster = line.split(',') temp = [] for value in data_cluster: temp.append(int(value)) self.matrix.append(temp) # 獲取資料下界 最小值之和 def get_low_limit(self): for i in range(self.n): self.min += min(self.matrix[i]) # 獲取資料上界 貪心演算法 def get_up_limit(self): # 初始化工人使用標記 worker_mark = [] for i in range(self.n): worker_mark.append(0) # 貪心演算法 取得 近似最優解 for i in range(self.n): temp = self.matrix[i] min_value = 5000 index = 0 for k in range(self.n): if worker_mark[k] == 0 and min_value > temp[k]: min_value = temp[k] index = k worker_mark[index] = 1 # 標記工人是否被分配 self.max += min_value # 累積上限值 # 分支界限演算法 def branch_limit(self): if self.pt_flag == 0: # 從第一層開始 for i in range(self.n): time = self.matrix[0][i] if time <= self.max: # 沒達到上限,建立節點,加入佇列 node = Node() node.deep = 0 node.cost = time node.value = time node.worker = i self.pt_nodes.append(node) self.pt_flag = 1 while self.pt_flag == 1: # 永久迴圈 等佇列空了在根據條件判斷來結束 if len(self.pt_nodes) == 0: break temp = self.pt_nodes.pop(0) # 先進先出 present_node = temp total_cost = temp.cost present_deep = temp.deep # 初始化工人分配標記 worker_mark = [] for i in range(self.n): worker_mark.append(0) # 檢查本節點下的作業分配情況 worker_mark[temp.worker] = 1 while temp.father is not None: temp = temp.father worker_mark[temp.worker] = 1 if present_deep + 1 == self.n: # 最後一排的葉子節點 直接分配結果 if self.min_leaf_node is None: self.min_leaf_node = present_node else: if self.min_leaf_node.cost > present_node.cost: self.min_leaf_node = present_node else: children = self.matrix[present_deep + 1] # 檢查本節點的子節點是否符合進入佇列的要求 for k in range(self.n): if children[k] + total_cost <= self.max and worker_mark[k] == 0: node = Node() node.deep = present_deep + 1 node.cost = children[k] + total_cost node.value = children[k] node.worker = k node.father = present_node self.pt_nodes.append(node) # 輸出演算法執行的結果 def output_result(self): file = open(self.output_file,'a') temp = self.min_leaf_node file.write('最少的消耗為:' + str(temp.cost) + '\n') file.write('第'+str(temp.worker+1) + '位工人完成第'+str(temp.deep+1) + '份工作\n') while temp.father is not None: temp = temp.father file.write('第' + str(temp.worker + 1) + '位工人完成第' + str(temp.deep + 1) + '份工作\n') print('演算法執行結果以及寫入到檔案:', self.output_file) # 分支節點類 class Node: def __init__(self): self.deep = 0 # 標記該節點的深度 self.cost = 0 # 標記到達該節點的總消費 self.father = None # 標記該節點的父節點 self.value = 0 # 本節點的消費值 self.worker = None # 本節點的該任務由第幾位工人完成 # 主邏輯 # input_file = 'input_assign05_01.dat' # input_file = 'input_assign05_02.dat' input_file = 'input_assign05_03.dat' # output_file = 'output_01.dat' # output_file = 'output_02.dat' output_file = 'output_03.dat' # 初始化演算法執行類 worker = Worker(input_file, output_file) # 執行分支界限演算法 worker.branch_limit() # 輸出結果 worker.output_result()
輸入資料:
10,12,20,30,18 23,30,8,12,22 11,21,23,40,16 33,34,23,19,20 21,32,11,14,21
輸出結果:
最少的消耗為:65 第4位工人完成第5份工作 第5位工人完成第4份工作 第1位工人完成第3份工作 第3位工人完成第2份工作 第2位工人完成第1份工作