前言
建模时候遇到了一个多源路线最短问题,想到了floyd算法,尝试用python记录了下来,也算是python的一个学习过程。(实际上python上手还是挺容易的,python有一个强大的社区,你遇到不懂得问题只需要搜索,然后调用相关API即可)。
算法原理
Dijkstra
我们先回顾一下图论中另外一种算法:Dijkstra算法
在图论中,寻找最短路径除了Dijkstra算法以外,还有Floyd算法也非常经典,Floyd算法主要计算多元最短路径。
在求单元正权值最短路径时,我们会用Dijkstra算法来求解,算法思想也很简单——贪心算法:传送门
,Dijkstra算法的思想很容易接收,但实际上实现是非常麻烦的,而对于多源(n)结点最短路径中,Dijkstra算法也能求解,只需要将Dijkstra封装一下,然后执行n次即可,但是代码量巨大,对我来说实现困难。
Floyd
Floyd算法又称为插点法,是一种利用动态规划(dp)的思想寻找给定的加权图中多源点之间最短路径的算法。>
for k in range(0,92):
for i in range(0, 92):
for j in range(0, 92):
len=distance[i][k]+distance[k][j]
distance[i][j]=min(distance[i][j],distance[i][k]+distance[k][j])
如上,Floyd算法就是三层for循环第一层为一次遍历每个结点,二三层:如果ij两点之间的距离<i经过k再到j的距离
,那么就修改ij之间的距离。算法简单,但需要理解,简单来说就是对邻接矩阵进行遍历,所以我们在此之前需要提前构建好邻接矩阵(n阶的一个方阵)。
如果你已经有各个结点之间的关系对和各个结点的左边,那么用python构建邻接矩阵相当方便
如下例
我已从excel中获取了各个结点的编号和坐标,以[(编号,x坐标,y坐标)]
的形式存放在AllPoint
这个list中。共92个结点
接着我又从excel中获取了结点关系对,即两两直接相连结点的编号,以[(A结点编号,B结点编号)]
的形式存放在road
这个list中。共140条道路。
生成92阶 0方阵
#先生成0矩阵
relation_matrix = [[0 for i in range(92)] for j in range(92)]
# 转成int
for i in range(0, 92):
for j in range(0, 92):
relation_matrix[i][j] = (int)(relation_matrix[i][j])
生成邻接矩阵
遍历road列表,把直接相连的两个点的距离算出,改写进邻接矩阵
################# 道路关系邻接矩阵矩阵 ############## 92*92
for i in range(0,len(road)):
x_index=(int)(road[i][0])
y_index=(int)(road[i][1])
relation_matrix[x_index-1][y_index-1]=relation_matrix[y_index-1][x_index-1]=math.sqrt((AllPoint[x_index-1][1]-AllPoint[y_index-1][1])**2+(AllPoint[x_index-1][2]-AllPoint[y_index-1][2])**2)
将邻接矩阵正对角线置为0,其余不能直达的两点置为inf(无穷)
for i in range(0,92):
for j in range(0,92):
if relation_matrix[i][j]==0:
relation_matrix[i][j]=float("inf")
relation_matrix[i][i]=0
# 打印输出邻接矩阵
print(relation_matrix[i])
由于矩阵比较大,暂截一部分,可以看到邻接矩阵构造完毕,明显看出此为一个对称方阵
我们对邻接矩阵深拷贝进distance中,distance用来存放最终的各个点之间最短距离
distance=copy.deepcopy(relation_matrix)
准备工作就绪,我们开始执行我们的Floyd算法:
########################## 弗洛伊德算法 ##############
for k in range(0,92):
for i in range(0, 92):
for j in range(0, 92):
len=distance[i][k]+distance[k][j]
distance[i][j]=min(distance[i][j],distance[i][k]+distance[k][j])
for i in range(0,92):
距离矩阵输出
print(distance[i])
输出结果:
可以看到各点之间的距离都已算出
实际上,这种邻接矩阵的构造并不是很好,邻接矩阵是一个对称方正,这样构造矩阵多占用了一半的内存资源,造成浪费。
在跑程序的时候,如果矩阵长度太大,稍微多两层循环,matlab会报错matrix长度过大,java会栈溢出,python则会出现超时现象。如果你不想花费时间想其他算法(但也大多会出现同样情况),这时候就要对矩阵进行压缩处理。
邻接矩阵压缩存储
让我们再来回看一下邻接矩阵形式。
很显然,矩阵中的上三角或下三角是多余的。我们这里去掉上三角,保留下三角,并给他转换成一维的形式
(如果只是单纯对上三角赋空,还是用二维矩阵的方式存储,开辟空间不变,达不到优化效果)
我们很容易看出,第i行我们需要去前i个元素,存储到一维数组中。
我们稍微用一下脑子就可得出:
第i行j列的元素(注意:此处的ij是下标索引),转化到一维数组坐标为
i*(i-1)/2+j-1
具体转化代码如下图:
for i in range(matrix) :
for j in range(matirx):
matrix_new[i*(i-1)/2+j-1] = matrix[i][j]
}