1.简介
针对图中的最短路径计算,Dijkstra基本可以解决大部分问题,但是当图中有负权边出现的时候,该算法不再适用。为了解决该问题,提出了Bellman-Ford算法,适用范围是上去了,但是其复杂度为O(mn),其中m为图边数,n为图顶点数。为此,进行了相关的改进,有两种主要改进方案,其中有一个SPFA算法被广泛使用。
2.相关算法与优化
Bellman_Ford的流程(以计算从源s到图中其它顶点距离为例):
(i):初始化数组d[n],其中n为图的顶点个数;d[s]=0,其它任意顶点i的d[i]=∞
(ii):迭代进行松弛操作:循环n-1次:
遍历图中的每条边,对于任意边e(u,v),如果有d[u]>d[v]+w(u,v),则d[v]=d[v]+w(u,v)
(iii):检验负权回路:遍历图中的边,对于任意边e(u,v),如果d[v-1]>d[u-1]+w(u,v) ,则返回失败,否则正常返回
Bellman_Ford的改进1:步骤2中的n-1此迭代,导致了Bellman_Ford的效率不高,其中一个改进就是如果在其中一此循环中发现,没有发生松弛操作,则推出循环
Bellman_Ford的改进之SPFA,其思路是,松弛操作只可能产生在d发生变化的节点中,所以可以缩小进行松弛操作的范围,用队列来进行记录要进行松弛操作的节点:
(i):初始化数组d和队列sc;其中d[s]=0,其他的d元素为∞,sc为一个队列,将s入队
(ii):如果队列sc不为空,从中取出一个元素,遍历其与邻接点之间的边,如果邻接点发生松弛操作,而且不在队列sc中,则将该节点入队列;如果队列为空,则停止算法
基本上SPFA的复杂度为O(km),k《n
3.编程实例
import networkx as nx
def bellman_ford(i,G=nx.Graph()):
d=[]
for node in G.nodes():
d.append(100)
d[i-1]=0
j=1
while j<G.number_of_nodes():
relax=True
for edge in G.edges():
u=edge[0]
v=edge[1]
if d[v-1]>d[u-1]+G.get_edge_data(u,v)['weight']:
d[v-1]=d[u-1]+G.get_edge_data(u,v)['weight']
relax=False
if relax==True:
break
j=j+1
for edge in G.edges():
u=edge[0]
v=edge[1]
if d[v-1]>d[u-1]+G.get_edge_data(u,v)['weight']:
return None
return d
def SPFA(i,G=nx.Graph()):
d=[]
sc=[]
for node in G.nodes():
d.append(100)
d[i-1]=0
sc.append(i)
while sc:
node1=sc.pop()
for node in G.nodes():
if G.has_edge(node1,node) and d[node-1]>d[node1-1]+G.get_edge_data(node1,node)['weight']:
d[node-1]=d[node1-1]+G.get_edge_data(node1,node)['weight']
if node not in sc:
sc.append(node)
return d
distance=[]
G=nx.Graph()
G.add_weighted_edges_from([(1,2,8),(1,3,16),(1,5,13),(2,3,7),(2,4,17),(2,5,11),(2,6,10),(3,4,5),(4,5,14),(4,6,6)])
if bellman_ford(1,G):
distance=bellman_ford(1,G)
print distance
print SPFA(1,G)
输出:
参考:
http://www.cnblogs.com/AndreMouche/archive/2011/03/29/1998824.html
http://hi.baidu.com/kerrynit/blog/item/d1d90f33eec11dadd1a2d367.html