单源最短路径快速算法(spfa)的python3.x实现

Posted 雪域互联技术团队

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单源最短路径快速算法(spfa)的python3.x实现相关的知识,希望对你有一定的参考价值。

A.Star算法团队,专注于算法领域,欢迎大家交流与讨论。



单源最短路径快速算法(spfa)的python3.x实现

0. 写在最前面

最近比较忙呢,写的比较少了。抽空写了一下这篇文档,简陋勿喷~(后面准备做个算法包,包括基础的数据结构和算法,感觉任重而道远)

1. SPFA的简介[1]

SPFA(Shortest Path Faster Algorithm)算法是求单源最短路径的一种算法,它是Bellman-ford的队列优化,它是一种十分高效的最短路算法。 很多时候,给定的图存在负权边,这时类似Dijkstra等算法便没有了用武之地,而Bellman-Ford算法的复杂度又过高,SPFA算法便派上用场了。SPFA的复杂度大约是O(kE),k是每个点的平均进队次数(一般的,k是一个常数,在稀疏图中小于2)。 但是,SPFA算法稳定性较差,在稠密图中SPFA算法时间复杂度会退化。 实现方法:建立一个队列,初始时队列里只有起始点,在建立一个表格记录起始点到所有点的最短路径(该表格的初始值要赋为极大值,该点到他本身的路径赋为0)。然后执行松弛操作,用队列里有的点去刷新起始点到所有点的最短路,如果刷新成功且被刷新点不在队列中则把该点加入到队列最后。重复执行直到队列为空。 此外,SPFA算法还可以判断图中是否有负权环,即一个点入队次数超过N。

2. python实现[2]

 
   
   
 
  1. """

  2. _spfa_simple算法参考【xunalove】在csdn中的帖子

  3.    http://blog.csdn.net/xunalove/article/details/70045815

  4. _spfa_cut算法参考【火星十一郎】在cnblogs中的帖子

  5.    http://www.cnblogs.com/hxsyl/p/3248391.html

  6. """

  7. from queue import Queue

  8. import numpy as np

  9. npa = np.array

  10. npm = np.mat

  11. def spfa(s, node=0, inf=np.inf, method='simple'):

  12.    """

  13.    单源最短路的SPFA算法,Shortest Path Faster Algorithm

  14.    :param s:距离矩阵(邻接矩阵表示)其中s[i][j]代表i到j的距离

  15.    :param node:源点

  16.    :param inf:无穷大值

  17.    :param method:

  18.    'simple':简单算法,针对非负距离(注意不是非负环)有效

  19.    :return:

  20.    """

  21.    if method == 'simple':

  22.        return _spfa_simple(s, node, inf)

  23.    elif method == 'cut':

  24.        return _spfa_cut(s, node, inf)

  25.    else:

  26.        raise ValueError("method not found")

  27. def _spfa_simple(s, node=0, inf=np.inf):

  28.    """

  29.    单源最短路径算法,

  30.    只对非负权值有效

  31.    :param s: 距离矩阵(邻接矩阵表示)其中s[i][j]代表i到j的距离

  32.    :param node:源点

  33.    :return:

  34.    """

  35.    a = npa(s)

  36.    m, n = a.shape

  37.    if m != n:

  38.        raise ValueError("s 需要是方阵")

  39.    dis = np.ones(n) * inf

  40.    vis = np.zeros(n, dtype=np.int8)

  41.    dis[node] = 0

  42.    vis[node] = 1

  43.    que = Queue()

  44.    prenode = -np.ones(n, dtype=np.int8)  # 记录前驱节点,没有则用-1表示

  45.    que.put(node)

  46.    while not que.empty():

  47.        v = que.get()

  48.        vis[v] = 0

  49.        for i in range(n):

  50.            temp = dis[v] + a[v][i]

  51.            if a[v][i] > 0 and dis[i] > temp:

  52.                dis[i] = temp  # 修改最短路

  53.                prenode[i] = v

  54.                if vis[i] == 0:  # 如果扩展节点i不在队列中,入队

  55.                    que.put(i)

  56.                    vis[i] = 1

  57.    return dis, prenode

  58. def _spfa_cut(s, node=0, inf=np.inf):

  59.    """

  60.    单源最短路径算法,

  61.    只对非负环有效

  62.    :param s: 距离矩阵(邻接矩阵表示)其中s[i][j]代表i到j的距离

  63.    :param node:源点

  64.    :return:

  65.    """

  66.    a = npa(s)

  67.    m, n = a.shape

  68.    if m != n:

  69.        raise ValueError("s 需要是方阵")

  70.    count = np.zeros(n, dtype=np.int8)

  71.    dis = np.ones(n) * inf

  72.    vis = np.zeros(n, dtype=np.int8)

  73.    dis[node] = 0

  74.    vis[node] = 1

  75.    que = Queue()

  76.    prenode = -np.ones(n, dtype=np.int8)  # 记录前驱节点,没有则用-1表示

  77.    que.put(node)

  78.    while not que.empty():

  79.        v = que.get()

  80.        vis[v] = 0

  81.        for i in range(n):

  82.            temp = dis[v] + a[v][i]

  83.            if dis[i] > temp:

  84.                dis[i] = temp  # 修改最短路

  85.                prenode[i] = v

  86.                if vis[i] == 0:  # 如果扩展节点i不在队列中,入队

  87.                    count[i] += 1

  88.                    if count[i] > n:

  89.                        raise ValueError("输入有负环异常")

  90.                    que.put(i)

  91.                    vis[i] = 1

  92.    return dis, prenode

3. 改进和说明

  1. 现在的代码比较简陋,以后需要更多的补充。比如

  • 没有优先队列优化,可以参考参考资料[4]优化

  • 缺乏测试用例进行测试

  • 缺少与迪杰斯特拉(Dijkstra)算法和贝尔曼-弗洛伊德(Bellman-Ford)算法的比较

   2. 算法更试用于稀疏图,而且时间效率并不稳定[4]

4. 写在最后

把这块代码上传到pypi.org的时候发现居然404错,说我没有验证邮箱。可能由于pypi在海外的关系,为了等验证邮箱居然花了半个小时。不吐不快。

参考资料

[1] cnblogs SPFA算法 https://www.cnblogs.com/shadowland/p/5870640.html 2018-3-5 [2] 码云开源项目 python 3实现的spfa https://gitee.com/snowlandltd/snowland-algorithm-python/blob/master/SApy/graphtheory/spfa/_spfa.py 2018-3-5 [3] csdn 最快最好用的——spfa算法 http://blog.csdn.net/xunalove/article/details/70045815 2018-3-5 【比较推荐这篇,用C++和Pascal实现的】 [4] cnblogs SPFA算法学习笔记 http://www.cnblogs.com/hxsyl/p/3248391.html 2018-3-5 【同样推荐这篇,java实现,而且用优先队列优化】

您的支持是我们努力的动力,赞赏码在这里,欢迎打赏^_^



以上是关于单源最短路径快速算法(spfa)的python3.x实现的主要内容,如果未能解决你的问题,请参考以下文章

探秘SPFA——强大的单源最短路径算法

SPFA算法_带负权边的单源最短路径

单源最短路径算法总结

图论-单源最短路-SPFA算法

[模板]洛谷T3371 单源最短路径 SPFA+手工队列类

单源最短路径算法