网络最短路——Floyd算法实现

Floyd算法(Floyd-Warshall算法)是一种用于求解图中所有顶点对之间最短路径的算法,该算法名称以创始人之一、1978年图灵奖获得者、斯坦福大学计算机科学系教授罗伯特·弗洛伊德命名。Floyd算法可以应用于许多方面,特别是在交通、物流和通信网络的优化中,譬如城市交通规划:Floyd算法可以帮助规划城市交通网络,确定最短路径和最优路线,以减少交通拥堵和行程时间。物流管理:在物流领域,Floyd算法可以用于确定不同地点之间的最短路径,帮助优化货物运输和配送的路线,降低成本并提高效率。电信网络规划:Floyd算法可应用于电信网络中的路由优化,帮助确定数据包传输的最短路径,提高网络连接的速度和质量。航班路径规划:航空公司可以使用Floyd算法确定不同机场之间的最短路径,优化航班计划和航线,提高飞行效率和旅客体验。金融交易网络:在金融领域,Floyd算法可以用于分析银行间的交易网络,确定最短路径和关键参与者,以便监测风险和进行迅速的资金清算,等等。

一、最短路问题

若网络中的每条边都有一个数值(长度、成本、时间等),则找出两节点(通常是源节点和阱节点)之间总权和最小的路径就是最短路问题。最通用的最短路问题(认为是标准型)可以如此描述:希望在网络中找到一条从源节点(source node)到接收节点(target node)的最小成本路径,这里的最小成本可定义为路径长度、旅行时间、旅行费用等。
G是由节点集合V(元素个数为n)和弧集合A(元素个数为m)组成的网络,定义节点sV为源节点(source),其他节点为非源节点(non-source),路径长度为该路径所包含弧的长度之和。求解单源最短路径问题就是找出源节点s到每一个非源节点t的有向最短路径。最短路径问题的数学模型如下:

 Minimize (i,j)Acijxij s.t. {j:(i,j)A}xij{j:(j,i)A}xji={1, if i=s1, if i=t0, otherwise xij:01 决策变量, xij=1 表示经过弧 (i,j),xij=0 表示不经过弧 (i,j).

二、最短路的Floyd算法

求图上两点i,j 之间的最短距离,可以按“从小图到全图”的步骤,在逐步扩大图的过程中计算和更新最短路。想象图中的每个点是一个灯,开始时所有灯都是灭的。然后逐个点亮灯,每点亮一个灯,就重新计算i,j 的最短路,要求路径只能经过点亮的灯。所有灯都点亮后计算结束。在这个过程中,点亮第k个灯时,能用到1k1个亮灯的结果。

Floyd算法是经典的动态规划算法,基本思想是递推产生一个矩阵序列A1,A2,..,Ak,...,An(图有n个节点),Ak=[ak(i,j)]n×n。其中矩阵Aki行第j列表示从顶点vi到顶点vj的路径上经过的顶点序号不大于k的最短路径长度。
迭代公式ak(i,j)=min(ak1(i,j),ak1(i,k)+ak1(k,j))]
k是迭代次数,ijk=1,2,...,n
当最后k=n是时,An矩阵就是各个顶点之间的最短距离值了。
那么如果需要记录路径,那么需要引入路由矩阵。路由矩阵Rk=[rk(i,j)]n×n,用来记录两点之间路径的前驱后继的关系,其中rk(i,j)表示从顶点vi到顶点vj的路径经过编号为rk(i,j)的顶点。所以需要注意了,路由矩阵里面不是数值,而是结点。

以上理论听起来挺复杂的。不过没有关系的。真正的算法实现无外乎就是三个循环嵌套,i,j的循环是任意两个点,而k则是两个点之间所经过的第三个点,我们就是在循环之中不断比较从ij的距离与从ik距离加上从kj距离的大小,如果经过这个点,路径变短了,我们就接受这个点,认为可以经过这个点;否则就不经过这个点,就是从ij最短。
如果我们得到了路由矩阵,那怎样得到路径呢?我们得到的路由矩阵Rn,其中r(i,j)(就是路由矩阵第ij列)表示第i个节点到第j个节点最短路径经过的中间点p1。那么我们先向起始顶点vi反向追踪,得到r(i,p1)=p2,r(i,p2)=p3...,最终r(i,pn)=0,就结束了;再正向追踪r(p1,j)=q1,r(q1,j)=q2.....r(qt,j)=0,完成正向追踪。路径就是vi,pn,...,p2,p1,q1,q2,...,qt,vj

三、最短路Python求解

案例:考虑下图程序所作图的最短路。

3.1 作网络图

import numpy as np
import networkx as nx
import matplotlib.pyplot as plt

A = np.mat([[0, 3, 0, 2, 0],
            [3, 0, 2, 1, 0],
            [0, 2, 0, 0, 5],
            [2, 1, 0, 0, 4],
            [0, 0, 5, 4, 0]])  # Adjacency matrix

G = nx.Graph(A)  # Undirected weighted graph
pos = nx.shell_layout(G)  # Set the layout for node positions
w = nx.get_edge_attributes(G, "weight")  # Get the edge weights

nx.draw_networkx(G=G, pos=pos, node_size=260)  # Draw the network graph
nx.draw_networkx_edge_labels(G=G, pos=pos, font_size=12, edge_labels=w)  # Label the edge weights
plt.show()

3.2 最短路计算

import numpy as np
from numpy import inf

A = [[inf, 3, inf, 2, inf],
     [3, inf, 2, 1, inf],
     [inf, 2, inf, inf, 5],
     [2, 1, inf, inf, 4],
     [inf, inf, 5, 4, inf]]

n = len(A)  # Calculate the number of vertices in the graph.

dis = A  # Initialize the dis matrix with the given adjacency matrix A.
path = np.zeros((n, n))  # Initialize the path matrix with zeros.

for k in range(n):
    for i in range(n):
        for j in range(n):
            if dis[i][k] + dis[k][j] < dis[i][j]:
                dis[i][j] = dis[i][k] + dis[k][j]  # Update the shortest distance between i and j through k.
                path[i][j] = k  # Record the intermediate vertex k in the shortest path between i and j.

print("Shortest path distances (dis):")
print(np.array(dis))  # Display the matrix containing the shortest path distances between all pairs of vertices.

print("Intermediate vertices in shortest paths (path):")
print(np.array(path, dtype=int))  # Display the matrix representing the intermediate vertices in the shortest paths.
Shortest path distances (dis):
[[4 3 5 2 6]
 [3 2 2 1 5]
 [5 2 4 3 5]
 [2 1 3 2 4]
 [6 5 5 4 8]]
Intermediate vertices in shortest paths (path):
[[3 0 1 0 3]
 [0 3 0 0 3]
 [1 0 1 1 0]
 [0 0 1 1 0]
 [3 3 0 0 3]]

我们以0到4 点为例,最短距离是6。path[0][4]=3,该路径经过点3,反向追踪:path[0][3]=0,结束;正向追踪:path[3][4]=0,结束。最短路径就是0->3->4。看一看前面的图,的确符合事实。但是,实际上,这五个点最好用a,b,c,d,e等表示,或是1,2,3,4,5。这里的用做终止追踪的0其实是初始时的0,而这个矩阵当中也有作为节点标记的0,看的时候很容易混淆。如果我们用字母标记,最终会看到路由矩阵中有节点标记,也有数字0,看起来会方便很多。

四、最短路R求解

实例:求下图所示网络各顶点间的最短路。

4.1 作网络图

library(igraph)
data <- matrix(c(
  0, 30, 15, 0, 0, 0,
  5, 0, 0, 0, 20, 30,
  0, 10, 0, 0, 0, 15,
  0, 0, 0, 0, 0, 0,
  0, 0, 0, 10, 0, 0,
  0, 0, 0, 30, 10, 0
), nrow = 6, byrow = TRUE)

rownames(data) <- c("S1", "S2", "s3", "s4", "s5", "s6")
colnames(data) <- c("S1", "S2", "s3", "s4", "s5", "s6")

# Create the graph
graph <- graph.adjacency(data, mode = "directed", weighted = TRUE, diag = FALSE)
# Plot the graph with row names as labels
plot(graph, vertex.label = rownames(data), vertex.size = 30, vertex.color = "lightblue", edge.label = E(graph)$weight)

4.2 最短路计算

# 准备数据
data <- matrix(c(
  0, 30, 15, Inf, Inf, Inf,
  5, 0, Inf, Inf, 20, 30,
  Inf, 10, 0, Inf, Inf, 15,
  Inf, Inf, Inf, 0, Inf, Inf,
  Inf, Inf, Inf, 10, 0, Inf,
  Inf, Inf, Inf, 30, 10, 0
), nrow = 6, byrow = TRUE)

path <- matrix(0, nrow = 6, ncol = 6)

for (k in 1:6) {
  for (i in 1:6) {
    for (j in 1:6) {
      if (data[i, j] > data[i, k] + data[k, j]) {
        data[i, j] <- data[i, k] + data[k, j]
        path[i, j] <- k
      }
    }
  }
}

# 定义函数找出x到y的具体路径
show_trace <- function(x, y) {
  trace <- c()
  add_trace <- function(x, y) {
    if (path[x, y] != 0) {
      add_trace(x, path[x, y])
      trace <<- c(trace, path[x, y])
      add_trace(path[x, y], y)
    }
  }
  add_trace(x, y)
  if (length(trace) > 0) {
    trace_str <- paste(c(x, trace, y), collapse = "-->")
    cat(paste("从", x, "到", y, "的最短路径为:", trace_str))
  } else {
    cat(paste("从", x, "到", y, "不存在路径"))
  }
}

print(data)
show_trace(1, 5)  # 求S1到S5的最短路径
show_trace(1, 4)  # 求S1到S4的最短路径
     [,1] [,2] [,3] [,4] [,5] [,6]
[1,]    0   25   15   50   40   30
[2,]    5    0   20   30   20   30
[3,]   15   10    0   35   25   15
[4,]  Inf  Inf  Inf    0  Inf  Inf
[5,]  Inf  Inf  Inf   10    0  Inf
[6,]  Inf  Inf  Inf   20   10    015 的最短路径为: [1-->3-->6-->5]14 的最短路径为: [1-->3-->6-->5--> 4]

总结

弗洛伊德算法的算法实现比较简单,过程容易理解,但原理不是很容易消化透彻。同学们在学习的时候可以根据算法过程,找一个具体的图仔细捋一捋,画一下距离矩阵与路由矩阵变化过程,以及当中第三点的选取、距离的比较等,体会当中的动态规划思想。该算法可以求最短路径及其长度,但是对于复杂的网络,该算法还是有局限性的。这个时候我们便需要选择其它的自然算法,例如蚁群算法等来进行求解。虽然那些相对“高级”的算法不一定在众多的路径中找到最短的那一条,但是却可以在较短时间内找到比较短的一条路径,而且会很接近最短路径的。

参考文献

  1. 最短路问题与标号算法(label correcting algorithm)研究(2)
  2. Floyd算法
  3. 弗洛伊德(Floyd)算法详解
posted @   郝hai  阅读(528)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
历史上的今天:
2022-06-27 参数估计与假设检验—统计学(八)
2022-06-27 统计学(四)——描述性统计指标计算
2022-06-27 统计学(三)——数据可视化表达
点击右上角即可分享
微信分享提示