long long ago就已经知道了Floyd算法,关键代码就4行,也容易记住,上上周又看到了Floyd,都说是动态规划,所以特意去学了一圈动态规划,今天终于又回到了它
d[k][i][j]定义:“只能使用第1号到第k号点作为中间媒介时,点i到点j之间的最短路径长度。”
在动态规划算法中,处于首要位置、且也是核心理念之一的就是状态的定义
这个大家喜欢把它叫做“松弛操作”,也就是relax
对于d[k][i][j](即使用1号到k号点中的所有点作为中间媒介时,i和j之间的最短路径),可以分为两种情况:
1)i到j的最短路不经过k;(
2)i到j的最短路经过了k。不经过点k的最短路情况下,d[k][i][j]=d[k-1][i][j]。经过点k的最短路情况下,d[k][i][j]=d[k-1][i][k]+d[k-1][k][j]。
因此,综合上述两种情况,便可以得到Floyd算法的动态转移方程:
d[k][i][j] = min(d[k-1][i][j], d[k-1][i][k]+d[k-1][k][j])(k,i,j∈[1,n])
初始条件:d[0][i][j]=w(i, j),d[k][0][j]=0,d[k][i][0]=0
# d[k][i][j] = min(d[k-1][i][j], d[k-1][i][k]+d[k-1][k][j])(k,i,j∈[1,n])
import numpy as np
def floyd_original(graph):
vertex_num = len(graph)
list = np.full((vertex_num+1,vertex_num+1,vertex_num+1),np.inf)
list[:,0,:] = 0
list[:,:,0] = 0
for i in range(1,vertex_num+1):
for j in range(1,vertex_num+1):
list[0,i,j] = graph[i-1,j-1]
for k in range(1,vertex_num+1):
for i in range(1,vertex_num+1):
for j in range(1,vertex_num+1):
list[k,i,j] = min(list[k-1,i,j],list[k-1,i,k]+list[k-1,k,j])
return list[vertex_num,1:,1:]
d[k][i][k]
= min(d[k-1][i][k], d[k-1][i][k]+d[k-1][k][k])
= min(d[k-1][i][k], d[k-1][i][k]+0)
= d[k-1][i][k]
也就是说在第k-1阶段和第k阶段,点i和点k之间的最短路径长度是不变的。相同可以证明,在这两个阶段中,点k和点j之间的的最短路径长度也是不变的。因此,对于使用滚动数组的转移方程d[i][j] = min(d[i][j], d[i][k]+d[k][j])来说,赋值号右侧的d[i][j], d[i][k]和d[k][j]的值都是上一阶段(k-1阶段)的值,可以放心地被用来计算第k阶段时d[i][j]的值。
有没有很坑? 绕了一圈又回到不熟悉的东西上去了
仔细看一遍上述的公式就懂了。
所以我们可以使用滚动数组来优化空间。
import numpy as np
def floyd(graph):
vertex_num = len(graph)
list = np.zeros((vertex_num+1,vertex_num+1))
for i in range(1,vertex_num+1):
for j in range(1,vertex_num+1):
list[i,j] = graph[i-1,j-1]
for k in range(1,vertex_num+1):
for i in range(1,vertex_num+1):
for j in range(1,vertex_num+1):
list[i,j] = min(list[i,j],list[i,k]+list[k,j])
return list[1:,1:]
#%%
graph = np.full((7,7),np.inf)
graph[0,:3] = [0,1,2]
graph[1,:4] = [1,0,3,4]
graph[2,:4] = [2,3,0,6]
graph[3,1:5] = [4,6,0,7]
graph[4,3:6] = [7,0,9]
graph[5,4:7] = [9,0,10]
graph[6,5:7] = [10,0]
print floyd_original(graph)
print floyd(graph)
[[ 0. 1. 2. 5. 12. 21. 31.]
[ 1. 0. 3. 4. 11. 20. 30.]
[ 2. 3. 0. 6. 13. 22. 32.]
[ 5. 4. 6. 0. 7. 16. 26.]
[12. 11. 13. 7. 0. 9. 19.]
[21. 20. 22. 16. 9. 0. 10.]
[31. 30. 32. 26. 19. 10. 0.]]
[[ 0. 1. 2. 5. 12. 21. 31.]
[ 1. 0. 3. 4. 11. 20. 30.]
[ 2. 3. 0. 6. 13. 22. 32.]
[ 5. 4. 6. 0. 7. 16. 26.]
[12. 11. 13. 7. 0. 9. 19.]
[21. 20. 22. 16. 9. 0. 10.]
[31. 30. 32. 26. 19. 10. 0.]]