最短路径:对于网图来说,是指两顶点之间经过的边上权值之和最少的路径,并且我们称路径上的第一个顶点是源点,最后一个顶点是终点。
迪杰斯特拉 ( D i j k s t r a ) (Dijkstra) (Dijkstra)
迪杰斯特拉 ( D i j k s t r a ) (Dijkstra) (Dijkstra):是一个按路径长度递增的次序产生最短路径的算法。
原理:假设 S S S为已求得的从源点出发的最短路径长度的顶点的集合,则可证明:下一条次最短路径(设其终点为 x x x)要么是弧 ( v , x ) (v,x) (v,x),要么是从源点出发的中间只经过 S S S中的顶点而最后到达顶点x的路径。
算法实现:
1.先设一个辅助列表
D
i
s
Dis
Dis用来记录自源点
v
0
v_0
v0到各个顶点的最短路径长度,设
D
i
s
Dis
Dis的初始值为
D
i
s
[
i
]
=
l
e
n
(
v
0
,
v
i
)
Dis[i]=len(v_0,v_i)
Dis[i]=len(v0,vi)。
2.分别设
S
S
S为已找到最短路径的顶点集合,
U
U
U为未找到最短路径的顶点集合。对
D
i
s
[
i
]
(
v
i
∈
U
)
Dis[i](v_i\in U)
Dis[i](vi∈U)的值进行替换:
D
i
s
[
i
]
=
m
i
n
{
D
i
s
[
j
]
+
l
e
n
(
v
j
,
v
i
)
,
D
i
s
[
i
]
}
,
(
v
j
∈
S
,
v
i
∈
U
)
\\Dis[i]=min\{Dis[j]+len(v_j,v_i),Dis[i]\},(v_j\in S,v_i\in U)
Dis[i]=min{Dis[j]+len(vj,vi),Dis[i]},(vj∈S,vi∈U),
若
i
0
,
s
.
t
.
D
i
s
[
i
0
]
=
m
i
n
{
D
i
s
[
i
]
}
(
v
i
∈
U
)
i_0,s.t.Dis[i_0]=min\{Dis[i]\}(v_i\in U)
i0,s.t.Dis[i0]=min{Dis[i]}(vi∈U),则我们便找到了由
v
0
v_0
v0到
v
i
0
v_{i_0}
vi0的最短的路径长度,此时将
v
i
0
v_{i_0}
vi0添加进
S
S
S,
v
i
0
v_{i_0}
vi0从
U
U
U中删除即可。
3.反复进行上述操作,直至
U
U
U为空。
Note:上述做法只能求得最短路径的长度这一数值,但是无法得知具体行进路径,若想要得到具体路径,可增加一个变量
T
r
a
c
e
Trace
Trace来记录行进过程。
代码以及测试数据
# -*- coding: utf-8 -*-
"""
Created on Sat Mar 30 09:53:07 2019
@author: Administrator
"""
from numpy import *
def Dijkstra(Mat,v):
#邻接矩阵matrix,起点v
#默认图连通
L=len(Mat) #顶点个数
Dis=list(Mat[v]) #表示起点到其余顶点的距离
Min=v #表示从那些未访问的顶点中所找到的最短路径所对应的顶点标号
S=[] #已访问的顶点集合
U=list(range(L)) #未访问的顶点集合
S.append(v) #先访问起点
U.remove(v)
Trace=[[None]]*L #储存具体路径
for x in range(L): #初始化路径长度列表
if Mat[v][x]!=inf:Trace[x]=[v,x]
Trace[v]=[v]
while(len(U)>0): #若还存在未访问的顶点,则继续循环
D=[]
for x in U:
#重新设置Dis
if Dis[Min]+Mat[Min][x]<Dis[x]:
Dis[x]=Mat[x][Min]+Dis[Min]
Trace[x]=Trace[Min]+[x]
D.append([x,Dis[x]])
#寻找次短路径
Min=sorted(D, key=lambda x:x[1])[0][0]
S.append(Min)
U.remove(Min)
return Dis,Trace
Mat=array([[ 0,10,inf,inf,inf,11,inf,inf,inf]
,[10,0,18,inf,inf,inf,16,inf,12]
,[inf,18,0,22,inf,inf,inf,inf,8]
,[inf,inf,22,0,inf,inf,24,16,21]
,[inf,inf,inf,inf,0,26,inf,7,inf]
,[11,inf,inf,inf,26,0,17,inf,inf]
,[inf,16,inf,24,inf,17,0,19,inf]
,[inf,inf,inf,16,7,inf,19,0,inf]
,[inf,12,8,21,inf,inf,inf,inf,0]])
Dis,Trace=Dijkstra(Mat,0)
弗洛伊德 ( F l o y d ) (Floyd) (Floyd)
弗洛伊德 ( F l o y d ) (Floyd) (Floyd)算法是通过在邻接矩阵中不断地尝试比较在两个顶点间的各条路径来得到最短路径的,即尝试不断地加入中转的顶点。
算法实现:
1.从图的带权邻接矩阵
A
=
(
a
i
,
j
)
n
×
n
,
(
i
,
j
=
1
,
2
,
.
.
.
,
n
−
1
)
A=(a_{i,j})_{n×n},(i,j=1,2,...,n-1)
A=(ai,j)n×n,(i,j=1,2,...,n−1)开始,递归地进行
n
n
n次更新,即由矩阵
D
(
0
)
=
A
D(0)=A
D(0)=A,按一个公式,构造出矩阵
D
(
1
)
D(1)
D(1);又用同样地公式由
D
(
1
)
D(1)
D(1)构造出
D
(
2
)
D(2)
D(2);…;最后又用同样的公式由
D
(
n
−
1
)
D(n-1)
D(n−1)构造出矩阵
D
(
n
)
D(n)
D(n)。矩阵
D
(
n
)
D(n)
D(n)的
i
i
i行
j
j
j列元素便是
v
i
v_i
vi到
v
j
v_j
vj的最短路径长度,称
D
(
n
)
D(n)
D(n)为图的距离矩阵。
2.假设已经有了
D
[
m
]
D[m]
D[m],下面阐述构造
D
[
m
+
1
]
D[m+1]
D[m+1],对于
D
[
m
]
D[m]
D[m]的的元素
a
i
,
j
a_{i,j}
ai,j,将
D
[
m
+
1
]
D[m+1]
D[m+1]的元素
b
i
,
j
b_{i,j}
bi,j更新为
b
i
,
j
=
m
i
n
{
a
i
,
k
+
a
k
,
j
}
(
k
=
0
,
1
,
2
,
.
.
.
,
n
−
1
)
b_{i,j}=min\{a_{i,k}+a_{k,j}\}(k=0,1,2,...,n-1)
bi,j=min{ai,k+ak,j}(k=0,1,2,...,n−1),对每个
a
i
,
j
a_{i,j}
ai,j进行同样的更新,便可得到
D
[
m
+
1
]
D[m+1]
D[m+1]。
3.直到更新至
D
[
n
]
D[n]
D[n]。
Note:同样,上述方法无法得到具体路径,因此额外设一个变量
P
[
0
]
P[0]
P[0],在计算
D
[
m
]
D[m]
D[m]时同时计算
P
[
m
]
P[m]
P[m],需要注意的是初始的
P
[
0
]
P[0]
P[0]的设定:
P
[
0
]
=
[
[
i
f
o
r
i
i
n
r
a
n
g
e
(
L
)
]
f
o
r
j
i
n
r
a
n
g
e
(
L
)
]
P[0]=[[i\ for\ i\ in\ range(L)]\ for\ j\ in\ range(L)]
P[0]=[[i for i in range(L)] for j in range(L)],即初始的
P
P
P中元素表示
v
i
v_i
vi走向
v
j
v_j
vj时,路径的终点为
v
j
v_j
vj。还需要注意的时如何通过最终得到的
P
P
P是读取具体路径的:若
P
=
(
p
i
,
j
)
n
×
n
,
(
i
,
j
=
1
,
2
,
.
.
.
,
n
−
1
)
P=(p_{i,j})_{n×n},(i,j=1,2,...,n-1)
P=(pi,j)n×n,(i,j=1,2,...,n−1),则
p
i
,
j
=
i
o
r
j
p_{i,j}=i\ or\ j
pi,j=i or j表示由
v
i
v_i
vi直接通过弧
(
v
i
,
v
j
)
(v_i,v_j)
(vi,vj)走向
v
j
v_j
vj,若
p
i
,
j
=
k
a
n
d
k
!
=
i
o
r
j
p_{i,j}=k\ and\ k!=i\ or\ j
pi,j=k and k!=i or j,则表示
v
i
v_i
vi号顶点走向
v
j
v_j
vj号顶点的最短路径中存在中转点
v
k
v_k
vk,接着再考察
p
i
,
k
p_{i,k}
pi,k与
p
k
,
j
p_{k,j}
pk,j,一直进行下去。
代码以及测试数据
# -*- coding: utf-8 -*-
"""
Created on Sat Mar 30 20:13:26 2019
@author: Administrator
"""
from numpy import *
def Floyd(Mat):
L=len(Mat)
D=array(Mat)
P=[[i for i in range(L)] for j in range(L)]
# print(P)
for k in range(L):
for i in range(L):
for j in range(L):
if D[i][j]>D[i][k]+D[k][j]:
D[i][j]=D[i][k]+D[k][j]
P[i][j]=k
# print(i,j,k)
# print(P)
return D,P
Mat=array([[ 0,10,inf,inf,inf,11,inf,inf,inf]
,[10,0,18,inf,inf,inf,16,inf,12]
,[inf,18,0,22,inf,inf,inf,inf,8]
,[inf,inf,22,0,inf,inf,24,16,21]
,[inf,inf,inf,inf,0,26,inf,7,inf]
,[11,inf,inf,inf,26,0,17,inf,inf]
,[inf,16,inf,24,inf,17,0,19,inf]
,[inf,inf,inf,16,7,inf,19,0,inf]
,[inf,12,8,21,inf,inf,inf,inf,0]])
D,P=Floyd(Mat)