最小树问题是一类非常简单的网络最优化问题,它在许多网络设计问题中有着广泛的应用。
常用的求最小树的算法有:破圈法、避圈法、边割法和Dijkstra算法等等。其中,破圈法、避圈法和边割法简单明了,便于在图上操作,但是破圈法和避圈法都需要判断圈,而边割法又要求构造边割,这在计算机中处理起来有些不便,而Dijkstra算法是一种易于在计算机上实现的有效算法。
Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,是由荷兰计算机科学家艾兹赫尔·戴克斯特拉在1956年发现的算法,并于3年后在期刊上发表。戴克斯特拉算法使用类似广度优先搜索的方法解决赋权图的单源最短路径问题。
Dijkstra算法是边割法的一种特殊形式,它与边割法不同之处在于给G的顶点(除顶点 外)都赋上了一个标号。
Dijkstra算法可以通过权矩阵上的计算来实现。给定一个n阶简单的赋权图G,它的权函数 就决定了一个权矩阵 ,其中 ;若 ,则 ;若 ,则 。
最小树权矩阵法
Step0:划掉矩阵 的第一列(即把第一列的所有元素都改为X),并把第1行中剩下的每个元素画下划线。
Step1:在有下划线的元素中找一个最小的元素 。若 ,停止,G中不存在支撑树;否则,把 圈起来,然后把第 列其他元素都改为X,并把第 行没有划掉的元素画下划线。
Step2:若所有的元素或者被圈起来或者被划掉,则结束,圈起来的元素对应于最小树的边;否则转Step1。
依据上述Dijkstra的算法步骤,在Python中进行实现。
我的基本思想是: 额外构造一个转移矩阵(transfer_matrix)用来存放最小树的权重,而在初始矩阵( graph_matrix )中 进行 算法操作。
矩阵中 用999表示;修改为X的操作,则修改为998。
对初始矩阵进行操作,直到初始矩阵中的最小值为998时,输出转移矩阵(即最小树的权重)以及最小树的信息。
Dijkstra算法的Python实现
import numpy as npimport matplotlib.pyplot as pltimport networkx as nxclass Solve_Dijkstra(object): def __init__(self): self.raw = [] #缓存矩阵索引 self.path = [] #最小生成树路径 self.m = 0 self.n = 0 # dijkstra算法实现 def dijkstra(self, graph_list): # 判断图是否为空,如果为空直接退出 if graph_list is None: return None # print(len(graph)) #构造一个转移矩阵用来存放最小生成树的权重 transfer_matrix = np.zeros([len(graph_list), len(graph_list)]) # print(transfer_matrix) # 格式化矩阵 graph_matrix = np.array(graph_list) self.Draw_graph(graph_matrix) # raw = [] # path = [] # path_dic = {} # m = 0 # n = 0 for i in range(len(graph_matrix)): # a = np.delete(graph_matrix, m, axis=1) # 把第一列所有元素改为X graph_matrix[:, self.m] = 998 # print('graph_matrix', graph_matrix) if graph_matrix.min() == 998: print('权重矩阵:', transfer_matrix) print('权重总和:', transfer_matrix.sum()) print('最短路径:', self.path) self.Draw_graph(transfer_matrix) else: # print(transfer_matrix) self.raw.append(self.n) # print(raw) # 设置缓存矩阵 temp = graph_matrix[self.raw] # print(temp) r, c = np.where(temp == np.min(temp)) # print(r, c) if temp.min() == 999: print('图不存在支撑树') else: transfer_matrix[self.n, int(c[0])] = temp.min() self.path.append([self.n, int(c[0])]) # print(transfer_matrix) self.m = c[0] self.n = self.m # 绘制图 def Draw_graph(self, graph_matrix): # 构造一个网格图 Initial_Graph = nx.Graph() # 构建图的标签 for i in range(len(graph_matrix)): for j in range(len(graph_matrix)): if graph_matrix[i, j] < 998 and graph_matrix[i, j] > 0: Initial_Graph.add_edge(('v' + str(i)), ('v' + str(j)), weight=int(graph_matrix[i, j])) # Graph_labels[(i, j)] = graph_matrix[i, j] else: continue # for u, v, d in G.edges(data=True): # print(u, v, d['weight']) edge_labels = dict([((u, v,), d['weight']) for u, v, d in Initial_Graph.edges(data=True)]) # pos = nx.spring_layout(Initial_Graph) pos = nx.random_layout(Initial_Graph) nx.draw_networkx_edge_labels(Initial_Graph, pos, edge_labels=edge_labels, font_size=14) # 绘制图中边的权重 nx.draw_networkx(Initial_Graph, pos, node_size=400) plt.show()if __name__ == '__main__': graph_list = [ [999, 50, 30, 40, 25], [50, 999, 15, 20, 999], [30, 15, 999, 10, 20], [40, 20, 10, 999, 10], [25, 999, 20, 10, 999]] solve_dijkstra = Solve_Dijkstra() solve_dijkstra.dijkstra(graph_list)
算法计算输出结果如下所示:
参考资料
[1]谢政.网络最优化[M].北京:科学出版社,2014:33-34.
[2维基百科编者. 戴克斯特拉算法[G/OL].维基百科, 2020(20200909)[2020-09-09].https://zh.wikipedia.org/w/index.php?title=%E6%88%B4%E5%85%8B%E6%96%AF%E7%89%B9%E6%8B%89%E7%AE%97%E6%B3%95&oldid=61566127.
01.Python随笔|抓取QQ群成员头像
02.Python随笔|抓取研招网调剂信息
03.Python随笔 | Pandas入门(一)