學號: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份工作