匈牙利演算法指派問題的python程式實現
阿新 • • 發佈:2021-02-09
最近同樣在備戰數模,在演算法相關的書籍中瞭解到匈牙利演算法,心血來潮用python寫了一個程式實現,固然有重複造輪子之嫌(已經有第三方庫可以很方便地解決問題),但作為練手之作,姑且聊博大家一笑吧。也希望能給同樣借用這個演算法練手的其他人一個啟發。
關於匈牙利演算法,相信會點進這篇文章的各位都有所瞭解,但是以防萬一,還是做一次贅述。
例如,對於一個n行n列的矩陣,
![如這個5行5列的矩陣,這張圖是在另一篇文章中嫖的](https://img-blog.csdnimg.cn/2021020711090024.png#pic_center
這個演算法是為了尋找方陣中不同行不同列的五個數最小和而誕生的,實現過程為行歸約-列規約-試指派-畫蓋零線-更新矩陣-迴圈直至找到最優解。每一步的具體操作敘述過於繁瑣,且我講的也大概率不如其他文章清晰,就不獻醜了。直接上程式碼,除去註釋約有150行,我會分步說明。因為直接在python中輸入矩陣過於繁瑣,矩陣的輸入依靠的是讀取txt等檔案,檔案中數字必須用英文下,隔開,如:
6,8,7
3,9,2
這就是一個三維矩陣。檔案路徑在末尾主程序中。
import numpy as np
def cag(list):
'''將一個只含字串數字的列表轉化成只含浮點數的列表'''
a = 0
for i in list:
list[a] = float(i)
a += 1
return list
def matrix(file):
'''讀取檔案中的係數矩陣並生成一個相同的n維矩陣'''
f = open(file,'r',encoding='utf8')
n = 0
for i in f.readlines():
data1 = i.replace('\n','')
data2 = data1.split(',')
data = cag(data2)
if n == 0:
mat = np.array(data)
else:
mat = np.vstack((mat,data))
n += 1
if n == len(data):
break
f.close()
return mat
# 到這一步就可以讀取到一個n階方陣
def row_reduction(ndarray):
'''進行行歸約'''
n = 0
for i in ndarray:
num = i[0]
for x in i:
if x > num:
pass
else:
num = x
redu = num*np.ones(len(i))
ndarray[n] = ndarray[n] - redu
n += 1
return ndarray
def column_reduction(ndarray):
'''通過轉置和行歸約實現列歸約'''
a = ndarray.T
b = row_reduction(a)
c = b.T
return c
def count_zero(nadrray):
'''通過遍歷輸出矩陣行零的個數'''
num = 0
for i in nadrray:
if i == 0:
num += 1
return num
def order(nadrray):
'''確定圈零順序'''
global row_order
row_order = [-1 for _ in range(len(nadrray[0]))]
for x in range(len(nadrray[0])): #按圈零順序將行號填入row_order
row = 0 # 行數
num = 1000 #最小值
for i in nadrray:
if num >= count_zero(i) and (row not in row_order):
num = count_zero(i)
row_order[x] = row
row += 1
return row_order
#這裡的圈零順序即是根據每行0的個數由小到大排序
def deal_zero(nadrray):
'''處理零元素'''
global Independent_element,Non_independent_element,row_order
nadrray1 = nadrray.T
Independent_element = [] #獨立零元素座標
Non_independent_element = [] #非獨立零元素座標
for i in row_order:
spzero = 0
column = 0
for x in nadrray[i]:
if x == 0 and spzero == 0 and [i,column] not in Independent_element and [i,column] not in Non_independent_element:
Independent_element.append([i,column])
spzero += 1
num = 0
for y in nadrray1[column]:
if y == 0 and [num,column] not in Independent_element and [num,column] not in Non_independent_element:
Non_independent_element.append([num, column])
num += 1
elif x == 0 and spzero != 0 and [i,column] not in Independent_element and [i,column] not in Non_independent_element:
Non_independent_element.append([i,column])
column += 1
#處理零元素中,每一行圈到的零即為獨立零元素,獨立零元素所在行列的零元素為非獨立零元素,被選為非獨立零元素後,再讀取的時候就不可被選為獨立零元素
def line(nadrray):
'''劃蓋零線'''
global row_line,column_line
'''row_line和column_line中的數字即表示劃線了的行和列,
這裡我直接跳過了打勾,通過打勾和劃線之間的邏輯關係劃蓋零線,
需要注意的是,由於選取獨立零元素是先到先得的方式,
所以在方陣存在多個最優解的情況時,僅會輸出某一個解'''
row_line = []
column_line = []
for i in range(len(nadrray[0])):
for x in Independent_element:
if i == x[0] and i not in row_line:
row_line.append(i)
break
for i in range(len(nadrray[0])):
if i not in row_line:
column = 0
for x in nadrray[i]:
if x == 0 and column not in column_line:
column_line.append(column)
column += 1
nadrray1 = nadrray.T
for i in column_line:
tip = 0
for x in nadrray1[i]:
if x == 0 and [tip,i] in Independent_element:
row_line.remove(tip)
tip += 1
def adjust(nadrray):
'''調整矩陣'''
global adjustment
adjustment = []
for i in range(len(nadrray)):
for x in range(len(nadrray)):
if i not in row_line and x not in column_line:
adjustment.append(nadrray[i][x])
adjustment.sort()
for i in range(len(nadrray)):
for x in range(len(nadrray)):
if i not in row_line and x not in column_line and nadrray[i][x] != 0:
nadrray[i][x] = nadrray[i][x] - adjustment[0]
elif i in row_line and x in column_line and nadrray[i][x] != 0:
nadrray[i][x] = nadrray[i][x] + adjustment[0]
#到這裡,定義的方法已經足夠解決問題。
if __name__ == '__main__':
nadrray = row_reduction(matrix(r'C:\\Users\\ASUS\\Desktop\\匈牙利演算法.txt')) #這是檔案路徑
nadrray = column_reduction(nadrray)
row_order = order(nadrray)
deal_zero(nadrray)
while len(Independent_element) != len(nadrray[0]):
#這裡表示當獨立零元素的個數等於係數矩陣的維數時,得到最優解,程式結束,否則更新矩陣,重複執行。
line(nadrray)
adjust(nadrray)
nadrray = row_reduction(nadrray)
nadrray = column_reduction(nadrray)
row_order = order(nadrray)
deal_zero(nadrray)
print(Independent_element)
'''接下來演示程式的使用方法。程式中檔案路徑為
C:\\Users\\ASUS\\Desktop\\匈牙利演算法.txt,
開啟路徑表示的檔案,輸入矩陣如圖。'''
#![重申,間隔數字的必須為英文下,](https://img-blog.csdnimg.cn/20210207122154304.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NodXppamlu,size_16,color_FFFFFF,t_70#pic_center)
#然後右鍵run,輸出結果為![這五個座標即為最優解選取的五個數字,格式為[row,column]。得到座標後計算所需時間就極為簡單了。](https://img-blog.csdnimg.cn/20210207122506406.png#pic_center)