赞
踩
路的长度:赋权图的路经过的边的权和
最短路:从顶点 v i v_i vi到顶点 v j v_j vj的路中,长度最小的一条路。
顶点间的距离:顶点 v i v_i vi到顶点 v j v_j vj的最短路的长度。记作 d ( u 0 , v 0 ) d(u_0,v_0) d(u0,v0)
已知赋权无向图 G ( V , E , W ) G(V,E,W) G(V,E,W)
固定顶点 u o u_o uo ,求 u 0 u_0 u0到其余顶点 v v v的最短路及距离 d ( u 0 , v ) d(u_0,v) d(u0,v)
算法思想:最短路径的子路也是最短路。固定 u 0 u_0 u0,从近到远求出 u 0 u_0 u0到其余顶点之一 v v v的最短路和距离,直至遍历完所有顶点(或到某一特定顶点 v 0 v_0 v0 )。
记号说明:
记号 | 含义 |
---|---|
d n o w ( v ) d_{now}(v) dnow(v) | 从顶点 u 0 u_0 u0当前顶点 v v v的路径长度 |
p a r e n t s ( v ) parents(v) parents(v) | 最短路径中当前顶点 v v v的上一个顶点 |
S i S_i Si | 含i个顶点的最短路径顶点集 |
算法结束后, S i S_i Si为最短路径集。 d n o w ( v ) d_{now}(v) dnow(v)为距离集。
算法局限:要求权值为正数
% Dijkstra.m function [path, distance] = Dijkstra(G, u0, v0) % Dijkstra算法求最短路径 % 初始化参数 n = G.numnodes; nodes = 1:n; i = 0; d_now = ones(n, 1) * inf; d_now(u0) = 0; parents = ones(n, 1) * u0; in_s = u0; history = []; % 遍历所有终点或v0进入最短路径集 while (i ~= n - 1) && ~(ismember(v0, in_s)) not_in_s = setdiff(nodes, in_s); for v = not_in_s for u = in_s % 查找uv间的边 uv_index = G.findedge(u, v); if uv_index % 边存在则获取权 w = min(G.Edges.Weight(uv_index)); % 计算新的最短距离 d_now(v) = min([d_now(v), d_now(u) + w]); if d_now(u) + w <= d_now(v) % 距离发生改变 parents(v) = u; % 记录变化 history = [history; [u, v]]; end end end end % 寻找最短距离的顶点 tmp = d_now; tmp(in_s) = inf; [~, new_u] = min(tmp); new_u = new_u(1); % 添加最短路径顶点 in_s = union(new_u, in_s); i = i + 1; end % 倒溯最短路径 last = v0; path = v0; while last ~= u0 k = find(history(:, 2) == last); last = history(k(end), 1); path = [last, path]; end distance = d_now(v0); end
示例:
clc,clear % 输入图的顶点对 E=[ 1 2 2;1 3 8;1 4 1; 2 3 6;2 5 1;3 4 7; 3 5 5;3 6 1;3 7 2; 4 7 9;5 6 3;5 8 2; 5 9 9;6 7 4;6 9 6; 7 9 3;7 10 1;8 9 7; 8 11 9;9 10 1;9 11 2; 10 11 4]; % 创建并绘制图 G=graph(E(:,1),E(:,2),E(:,3)); figure=plot(G,"EdgeLabel",G.Edges.Weight,'EdgeLabelColor','green'); % 使用Dijkstra算法求得最短路径并在图中标出 [path,distance]=Dijkstra(G,1,11); highlight(figure,path,'EdgeColor','red','LineWidth',2,"EdgeLabelColor","red");
def Dijkstra(G, u0, v0): """利用Dijkstra计算最短路径和距离""" # 初始化参数 n = G.number_of_nodes() nodes = list(range(1, n + 1)) d_now = np.ones(n) * np.inf d_now[u0 - 1] = 0 parents = [u0 for i in range(n)] s = set() s.add(u0) history = [] i = 0 # 遍历所有终点或v0进入最短路径顶点集 while (i != n - 1) or (v0 not in s): not_s = set(nodes) - s for v in not_s: for u in s: try: # 检查顶点u,v间是否有边 w = G.edges[u, v]['weight'] except: pass else: d_now[v - 1] = min([d_now[v - 1], d_now[u - 1] + w]) if d_now[u - 1] + w <= d_now[v - 1]: # 顶点最短值改变,记录改变 parents[v - 1] = u history.append([u, v]) # 寻找当前的最小距离点,并加入s集合 tmp = d_now.copy() in_s_index = [i - 1 for i in s] tmp[in_s_index] = np.inf new_u = np.argmin(tmp) + 1 s.add(new_u) i = i + 1 # 拼接最终的最短路径 last = v0 path = [v0] history = np.array(history) while last != u0: k = np.where(history[:, 1] == last) last = history[k[-1][-1], 0] path.append(last) path = path[-1::-1] distance = d_now[v0 - 1] return path, distance
示例:
# 导库 import networkx as nx import numpy as np # 输入边和权 edges = [(1, 2, 2), (1, 3, 8), (1, 4, 1), (2, 3, 6), (2, 5, 1), (3, 4, 7), (3, 5, 5), (3, 6, 1), (3, 7, 2), (4, 7, 9), (5, 6, 3), (5, 8, 2), (5, 9, 9), (6, 7, 4), (6, 9, 6), (7, 9, 3), (7, 10, 1), (8, 9, 7), (8, 11, 9), (9, 10, 1), (9, 11, 2), (10, 11, 4)] # 创建空图并添加顶点和边权 G = nx.Graph() G.add_weighted_edges_from(edges) # 计算顶点位置 pos = nx.spring_layout(G) # 绘制无权图 nx.draw(G, pos, with_labels=True, font_size=14) # 绘制最短路径 path,distance=Dijkstra(G,1,11) shortest_edges=[] for i in range(np.size(path)-1): shortest_edges.append((path[i],path[i+1])) nx.draw_networkx_edges(G,pos,edgelist=shortest_edges,edge_color='red') # 追加绘制权 labels = nx.get_edge_attributes(G, 'weight') nx.draw_networkx_edge_labels(G, pos,edge_labels=labels,font_color="green")
已知赋权无向图 G ( V , E , W ) G(V,E,W) G(V,E,W)
求所有顶点对 ( u 0 , v 0 ) (u_0,v_0) (u0,v0)之间的最短距离及对应最短路径
算法思想:依次将每个顶点作为可能的中间点,插入所有的顶点对之间进行判断,若最短距离更新,则将该点记录下来。直到n次之后,得到所有顶点对的最短距离,并通过之间的记录反推最短路径。
记号说明:
记号 | 含义 |
---|---|
d i s t a n c e s distances distances | 距离变换矩阵。算法结束后则为顶点之间的最短距离矩阵 |
R R R | 路由矩阵。记录顶点间的中间点变化 |
算法局限:所有圈的权值和非负
% Floyd.m function [distances, paths] = Floyd(G) % Floyd算法求所有顶点对之间的最短路径 % 矩阵初始化 distances = G.adjacency('weighted'); n = length(distances); distances(distances == 0) = inf; distances(1:n + 1:end) = 0; R = zeros(n); for k = 1:n for i = 1:n for j = 1:n % 插点更新 if distances(i, k) + distances(k, j) < distances(i, j) distances(i, j) = distances(i, k) + distances(k, j); R(i, j) = k; end end end end distances = full(distances); % 复原路径 paths = returnPaths(R); for i = 1:n for j = 1:n if i ~= j % 添加起点和终点 paths{i, j} = [i paths{i, j} j]; else paths{i, j} = i; end end end end
% returnPaths.m function paths = returnPaths(R) % 根据路由矩阵返回最短路径矩阵 n = length(R); paths = cell(n); for i = 1:n for j = 1:n paths{i, j} = parse(i, j); end end % 递归推导最短路径 function path = parse(i, j) p = R(i, j); if p == 0 path = []; else left = parse(i, p); right = parse(p, j); path = [left, p, right]; end end end
示例:
clear,clc E=[ 1 2 2;1 3 8;1 4 1; 2 3 6;2 5 1;3 4 7; 3 5 5;3 6 1;3 7 2; 4 7 9;5 6 3;5 8 2; 5 9 9;6 7 4;6 9 6; 7 9 3;7 10 1;8 9 7; 8 11 9;9 10 1;9 11 2; 10 11 4]; G=graph(E(:,1),E(:,2),E(:,3)); n=G.numnodes; [distances,paths]=Floyd(G)
def returnPaths(R): """根据路由矩阵还原路径""" def parse(i, j): """递归解析i,j顶点最短路径""" p = R[i, j] if p == 0: path = [] else: path = [p] left = parse(i, p - 1) right = parse(p - 1, j) path.extend(right) for num in left[-1::-1]: path.insert(0, num) return path n, n = np.shape(R) paths = [[] for i in range(n)] for i in range(n): for j in range(n): paths[i].append(parse(i, j)) return paths def Floyd(G): """Floyd算法求所有顶点间的最短路径""" # 初始化矩阵 distances = nx.to_numpy_matrix(G) n, n = np.shape(distances) distances[distances == 0] = np.inf row, col = np.diag_indices_from(distances) distances[row, col] = 0 R = np.zeros((n, n), dtype=int) # 插点更新 for k in range(n): for i in range(n): for j in range(n): if distances[i, k] + distances[k, j] < distances[i, j]: distances[i, j] = distances[i, k] + distances[k, j] R[i, j] = k + 1 # 复原路径 paths = returnPaths(R) # 路径添加起点和终点 for i in range(n): for j in range(n): if i != j: paths[i][j].append(j + 1) paths[i][j].insert(0, i + 1) else: paths[i][j] = [i + 1] return distances, paths
示例:
# 导库 import networkx as nx import numpy as np import pandas as pd # 输入边和权 edges = [(1, 2, 2), (1, 3, 8), (1, 4, 1), (2, 3, 6), (2, 5, 1), (3, 4, 7), (3, 5, 5), (3, 6, 1), (3, 7, 2), (4, 7, 9), (5, 6, 3), (5, 8, 2), (5, 9, 9), (6, 7, 4), (6, 9, 6), (7, 9, 3), (7, 10, 1), (8, 9, 7), (8, 11, 9), (9, 10, 1), (9, 11, 2), (10, 11, 4)] # 创建空图并添加顶点和边权 G = nx.Graph() G.add_weighted_edges_from(edges) # Floyd算法 distances, paths = Floyd(G) n = G.number_of_nodes()
d_df = pd.DataFrame(distances, index=range(1, n + 1), columns=range(1, n + 1))
d_df
p_df = pd.DataFrame(paths, index=range(1, n + 1), columns=range(1, n + 1))
p_df
[path,distance] = shortestpath(G,u0,v0) # 求u0到v0的最短路径和最短距离
D=distances(G) # 求顶点对间的最短距离矩阵
import networkx as nx import pandas as pd path = nx.dijkstra_path(G, u0, v0) # Dijkstra算法求两点的最短路径列表 distance = nx.dijkstra_path_length(G, u0, v0) # Dijkstra算法求两点的最短距离 path = nx.shortest_path(G, u0, v0,weight='weight') # 求两点的最短路径列表 distance = nx.shortest_path_length(G, u0, v0,weight='weight') # 求两点的最短距离 distances = dict(nx.all_pairs_dijkstra_path_length(G)) # 求顶点对之间的最短距离字典 d_df = pd.DataFrame(distances).T d_df paths = dict(nx.all_pairs_dijkstra_path(G)) # 求顶点对之间的最短路径字典 p_df = pd.DataFrame(paths).T p_df
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。