SPFA算法理论体系终极论证

article/2025/10/23 15:41:03

SPFA相关详细论证

    • 历史事件
    • Bellman-Ford算法简述和证明
    • SPFA的正确代码
    • 段凡丁的贡献
    • 正确复杂度分析

历史事件

众所周知,SPFA是一种对Bellman-Ford算法的优化。国内业界首次提出是1994年西南交通大学的段凡丁在学报上发表的论文。但实际上早在1956年Bellman-Ford算法提出的论文里已经提到了用类似bfs的队列方式松弛,之后国外也有人用过。段凡丁只是起了个名字。另外,这篇论文漏洞很多(作者自己的观点几乎全是错的),而且作者思考比较简单(应该是限于当时国内的业界水平,因为从段老师后期的著作看,他挺厉害的)。所以没人承认SPFA是段凡丁提出的。国内第一次清晰正确提出的SPFA算法应该是2009年,中山纪念中学一位姜同学的国家集训队论文。但他毕竟是个高中生,写论文功力不太成熟。目前写的比较严谨的是华东师大可信实验室的SPFA论文。这些论文想读都可以直接百度搜到。

Bellman-Ford算法简述和证明

  • 设图有n个点,m条边。
  • SPFA的理论基础是 Bellman-Ford算法。Bellman-Ford算法的理论依据是每个边最多被松弛n-1次,否则有负权环。
  • 代码:
int Bellman_Ford(int x){for(int i=1;i<=n;i++){dist[i]=oo;}dist[x]=0;for(int i=1;i<n;i++){for(int j=1;j<=m;j++)if(dist[e[j].y]>dist[e[j].x]+e[j].w)dist[e[j].y]=dist[e[j].x]+e[j].w;}for(int j=1;j<=m;j++)if(dist[e[j].y]>dist[e[j].x]+e[j].w)return -1;return 1;	
}
  • 证明每个边最多被松弛n-1次,否则有负权环。如果源点到点x有最短路,路径经过的点数有限,那么路径经过边数数最多是n。所以只要证明源点到某点x的最短路径path,在 l e n ( p a t h ) len(path) len(path)轮松弛后一定能浮现出来。其中 l e n ( p a t h ) len(path) len(path)是最短路径path所包含的边数。
  • 这个用数学归纳法就行:首先源点在0轮就已经确定了(最短路直接是0),所以len=0时,0轮确定。如果第i轮松弛确定了path上y点的最短路 p a t h y path_y pathy p a t h y path_y pathy点数 l e n ( p a t h y ) = i len(path_y)=i len(pathy)=i。那么根据上面的代码,对 p a t h path path上的y下一个点x, e d g e ( x , y ) edge(x,y) edge(x,y)在第i+1轮一定会被做松弛判断。所以x的最短路在第i+1轮一定被搜到,且 l e n ( p a t h x ) = i + 1 len(path_x)=i+1 len(pathx)=i+1
  • 所以一个边数为 l l l的最短路,在 l l l轮松弛后一定会被搜到。
  • 而所有最短路经过的边数最多n-1,所以算法是对的。
  • 如果n-1轮松弛后还边能松弛,那说明最短路经过的边数是无限的,有负权环。
  • 这种算法虽然复杂度是O(nm)比迪杰斯特拉慢,但是可以跑有负权边的图,还能判断负环。

SPFA的正确代码

  • SPFA和Bellman-Ford原理一样,只是有条理地改变了松弛顺序。每次从队头取个点,松弛它的相邻点。每个点被松弛(重新进队)的次数至多是源点到它的最短路的边数,证明方法还是数学归纳法,和上面的Bellman-Ford没啥区别。
  • 一旦发现哪个点松弛到了n次,就说明有负环。
  • 正确代码:
int spfa(int x){for(int i=1;i<=n;i++){dist[i]=oo;inq[i]=0;}dist[x]=0;int head=1,tail=0;q[++tail]=x;inq[s]=1;while(head<=tail){int cur=q[head];int sz=con[cur].size();for(int i=0;i<sz;i++){edge NE=e[con[cur][i]];if(dist[NE.x]+NE.w<dist[NE.y]){dist[NE.y]=dist[NE.x]+NE.w;cunt[NE.y]++;if(cunt[NE.y]>=n)return -1;if(!inq[NE.y]){q[++tail]=NE.y;inq[NE.y]=1;}}}head++;inq[cur]=0;}return 1;
}

段凡丁的贡献

段凡丁的论文理论根基都是不对的。

  • 段凡丁的spfa算法:
    在这里插入图片描述
    这个算法并没有判负环的机制。有负环就停不下来了。再看看段凡丁对于spfa正确性的证明。
  • 段凡丁论文里的spfa正确性证明分成两部分:一定能找到最短路;还有算法能在有限次后收敛在最短路这两部分。在这里插入图片描述
    在这里插入图片描述
    作者给出的这个正确性证明就他的算法来说是正确的。这给了后人很大的启发,他做的事情是极其重要的,尽管他并没有想的足够深入。先不提他错误的复杂度,从他的代码和证明来看,他当时并没有想到每个点至多入队n-1次,没有想到负环的情况。所以他的代码在有负环时就停不下来了。再来看看他“’硬核”的复杂度分析:
    在这里插入图片描述
    这个就是作者主观臆断了。凭一堆实验就强行断定复杂度,而没有理论支撑,显然是不行的。可以构造图,把SPFA的复杂度卡成O(nm)。到这里段老师写论文的心态大概是:段老师读了一些图论的论文,受到一些启发,总结可以跑负权图的单源最短路算法,取名SPFA。然后复杂度有些精妙不太好理论分析,做了大量随机(至少没有针对性)实验后就主观断定复杂度O(m),这是理论最好复杂度。所以兴奋地认为找到了比迪杰斯特拉跑的还快,适用范围还更广的算法。所以就写了这篇论文。但不可否认的是,这给后来的中国学者提供了宝贵的经验,在1994年是难能可贵的。

正确复杂度分析

实际上,SPFA算法的最坏复杂度是θ(nm)的。

  • 算法复杂度是O(nm)很好理解,因为根据算法,每次取队头判断松弛的次数最多是n-1=O(n)。最多去多少次队头呢?这就是队列的累计总长度。最坏的是:每个点被他所有入度都松弛一次而入队,这样队列长度最大一共m。所以O(nm)是成立的。实际上现实中这两种最大情况不会发生。

  • 下面构造最坏的情况,使得复杂度是 θ ( n m ) θ(nm) θ(nm)
    在这里插入图片描述

    • 这个图构造方法:
      源点是1。
      i到i+1有长度1的边。
      1到i(3<=i<=n)分别有2*i+1的边。
      i(3=<i<=n)到 j(1<=j<=i-2)有+oo的边。
    • 所以模拟算法过程,首先i会被直接与源点相连那条边搜到,然后就是前面的点i-1出队一次,i被松弛入队一次。所以: n u m P o p ( i ) = n u m P o p ( i − 1 ) + 1 , n u m P o p ( 1 ) = 1 numPop(i)=numPop(i-1)+1,numPop(1)=1 numPop(i)=numPop(i1)+1,numPop(1)=1

      所以从1~n每个点的出队次数是:
      1 , 2 , 3 , 4 , . . . , i , . . . , n 1,2,3,4,...,i,...,n 1,2,3,4,...,i,...,n

      每次出队会扫描一遍出度,1~n的出度依次是:
      n − 1 , 1 , 2 , 3 , . . . , ( i − 1 ) , . . . n − 2 , n − 2 n-1,1,2,3,...,(i-1),...n-2,n-2 n1,1,2,3,...,(i1),...n2,n2

      所以总判断松弛次数是

    T = ∑ i = 1 n ( i − 1 ) ∗ i T=\sum_{i=1}^{n}(i-1)*i T=i=1n(i1)i
    注:严格按上面的规律求出的结果和上面求和公式有O(n)级别的差值(有头尾特殊的几个和别的规律不太一样),不影响复杂度计算,忽略不计了。
    T = ∑ i = 1 n ( i − 1 ) ∗ i = ∑ i = 1 n i ∗ i − ∑ i = 1 n i = n ( n + 1 ) ( 2 n + 1 ) 6 − n ( n + 1 ) 2 = θ ( n 3 ) T=\sum_{i=1}^{n}(i-1)*i=\sum_{i=1}^{n}i*i-\sum_{i=1}^{n}i\\=\\\frac{n(n+1)(2n+1)}{6} - \frac{n(n+1)}{2}=θ(n^3) T=i=1n(i1)i=i=1niii=1ni=6n(n+1)(2n+1)2n(n+1)=θ(n3)
    而这个图里m是 O ( n 2 ) O(n^2) O(n2)的。所以复杂度是θ(mn)的。
    另外,SPFA实际跑起来不一定比Bellman-Ford快。

  • 一道题的比较。我随便找了到最短路的题写了一发,结果如下:

    • SPFA的时间:在这里插入图片描述
    • Bellman-Ford的时间:在这里插入图片描述
    • 整体来看两者差不多,但是Bellman-Ford好像快一些。。。
  • 以上就是我知道的所有的内容了。


http://chatgpt.dhexx.cn/article/DpoUfZqL.shtml

相关文章

spfa判定负环

算法思想&#xff1a; 我们用数组记录每个结点的最短路径估计值&#xff0c;用邻接表来存储图G。 采取的方法是动态逼近法&#xff1a; 1、设立一个先进先出的队列用来保存待优化的结点。 2、优化时每次取出队首结点u&#xff0c;并且用u点当前的最短路径估计值对离开u点所指向…

SPFA算法详解——判断负权环

算法核心代码实现负权环判断负权环 根据松弛次数根据最短路径上的死循环 SPFA&#xff08;Shortest Path Faster Algorithm&#xff09;&#xff08;队列优化&#xff09;算法是求单源最短路径的一种算法。它是在Bellman-ford算法的基础上加上一个队列优化&#xff0c;减少了冗…

(算法基础)SPFA算法

适用情景 SPFA算法一般运用在求最短路问题当中的单源最短路中存在负权边的情况&#xff08;正权边如果出题人没有特别恶心去卡的话&#xff0c;也是ok的&#xff09;&#xff0c;SPFA算法还可以去判断图中是否存在负环。 时间复杂度 一般来讲是O(m)&#xff0c;m就是图当中的边…

透析SPFA算法(图例讲解)

SPFA算法是Bellman-Ford的队列优化&#xff0c;所以先介绍Bellman-Ford算法。 Dijkstra算法是处理单源最短路径的有效算法&#xff0c;但它局限于边的权值非负的情况&#xff0c;若图中出现权值为负的边&#xff0c;Dijkstra算法就会失效&#xff0c;求出的最短路径就可能是错的…

SPFA 算法详解( 强大图解,不会都难!)

适用范围&#xff1a;给定的图存在负权边&#xff0c;这时类似Dijkstra等算法便没有了用武之地&#xff0c;而Bellman-Ford算法的复杂度又过高&#xff0c;SPFA算法便派上用场了。 我们约定有向加权图G不存在负权回路&#xff0c;即最短路径一定存在。当然&#xff0c;我们可以…

OSPF NSSA区域路由的计算过程与FA值实验

概述&#xff1a; NSSA区域中不存在5类LSA&#xff0c;但是为了描述区域外的路由条目&#xff0c;则用7类LSA代替5类LSA。 7类LSA仅在NSSA区域中存在&#xff0c;当7类LSA离开NSSA区域进入普通区域时&#xff0c;可以转化为5类LSA。 NSSA-LSA报文格式和5类LSA相同&#xff1…

求不规则立方体表面积java_不规则立方体体积计算

展开全部 计算不规则的立体图形体积可62616964757a686964616fe78988e69d8331333431373866以把这个物体放入水中&#xff0c;用现在容积-未放入物体的容积就是体积或用放入物体后高-未放入物体*长*宽(1升1立方分米&#xff1b;1毫升1立方厘米)。 体积的国际单位制是立方米。一件…

冠层分析法(VCP)提取叶面积指数

文章目录 一、简介二、代码实现三、小结参考文献一、简介 该方法最早是由Omasa等人提出,是根据激光雷达光束与树木的接触频率来计算光束截获概率。总结来讲具体过程分为三步: 1、 从三维体素模型网格里判断是否有点,有点的网格标记为1,没点的网格标记为0,没点的网格表明冠…

SPFA例题

引入 SPFA的核心思想是优化dist[a]min(dist[a],dist[b]w)这个式子&#xff1b; 不难想到&#xff0c;如果我们想要优化dist[a]&#xff0c;那么只能从dist[b]w这个式子转移过来&#xff1b; 也就是说&#xff0c;只有dist[b]变小了&#xff0c;才有可能优化dist[a]&#xff…

OSPF中的SPF计算

OSPF域内路由计算&#xff1a;SPF计算 在1类LSA和2类LSA中&#xff0c;包括了拓扑信息和路由信息 Phase 1 &#xff1a;根据1类LSA中的P2P、TransNet、2类LSA&#xff0c;构建SPF树 Phase 2&#xff1a;根据1类LSA中的Stub、2类LSA&#xff0c;计算最优路由 左边是拓扑图&am…

聊一聊有限体积法

导读&#xff1a;有限体积法的工作原理。 一、求解对象 首先提一下我们的主角Navier-Stokes方程&#xff1a; 为了能够通过SIMPLE算法求解Navier-Stokes方程&#xff0c;我们需要将该方程改写为下列矩阵形式的方程&#xff1a; 其中为了系数矩阵&#xff0c;是每个网格未知…

IDEA中新导入的项目找不到maven project解决办法

刚导入的项目&#xff0c;导入进来之后&#xff0c;找不到maven project&#xff0c;idea打开view ->tool windows下也没有maven模块。查看了setting下plugins中maven插件也在。最后发现项目在导入过程中&#xff0c;因为选择的原因&#xff0c;没有指定位maven工程&#xf…

win10删除文件提示'找不到该项目'

win10系统下删除文件时出现了"找不到该项目"问题 遇到一个问题: win10系统下删除文件时出现了"找不到该项目"问题 百度搜索后得到 DEL /F /A /Q \\?\%1 RD /S /Q \\?\%1 这两行dos命令 创建(.txt)文件后写入这两行命令,保存,修改(.txt)至(.bat) 最后…

IDEA项目下不显示target目录或者target目录不完整没有新添加的资源

1.首先解决不显示target目录的问题 需要知道:如果不是maven工程,是没有target目录的,其次编译后才会生成target目录. 普通项目会生成out目录 a>如果设置过隐藏target目录 只需要找到settings-->Editor-->File Type-->Ignore Files and Folder删除该项 b>如果…

项目启动报错,找不到文件

java.io.FileNotFoundExceptionE:\software\apache-maven-3.6.1-bin\apache-maven-3.6.1\localrepository\xalan\xalan\2.7.0\xercesImpl.jar (系统找不到指定的文件。) java.io.FileNotFoundExceptionE:\software\apache-maven-3.6.1-bin\apache-maven-3.6.1\localrepository\…

idea项目类存在却找不到

这种情况&#xff0c;我真是奇了怪了&#xff0c;服了。我试了一下&#xff0c;试出了解决方案&#xff1a;将这个类移到同级另一个包&#xff0c;看见不报错了&#xff0c;再移回去。属于给idea虚晃一枪&#xff0c;再运行…emmmmm也还是报错的。于是不断的删iml文件&#xff…

Windows 删除”找不到该项目”文件夹

问题根源&#xff1a; 使用不可显示ASCII字符或采用UNICODE字符方法创建的文件或文件夹&#xff1b; 名称中含有..等特殊符号文件或文件夹名称不符合Windows命名规范或建立空格目录名创建的文件或文件夹&#xff1b; 使用下载工具创建的文件夹,在未下载完成前自行删除文件或…

idea找不到项目目录

有时候idea拉取项目时出现没有目录&#xff0c;src.main等等&#xff0c;下面就来带大家找到项目目录并显示出来 首先我们打开文件&#xff08;在左上角——找到项目结构&#xff09; 然后我们找到模块——点击左边那个加号,然后选择自己的项目目录。再选择一下jdk版本&#x…

【常见问题】vs 找不到导入的项目,请确认import声明

问题描述 当复制人家的vs项目文件到自己电脑上进行调试&#xff0c;重新生成解决方案失败&#xff0c;并报如下错误。 解决方法 1.分析报错内容&#xff0c;出现问题文件为vcxproj文件。 2.找到vcxproj文件&#xff0c;用记事本打开。 3.CTRLF搜索“Import”&#xff0c…

无法删除提示找不到该项目,该项目不在C:\users\?win10无法强力删除文件?

无法删除提示找不到该项目,该项目不在C:\users\?win10无法强力删除文件? 新建个TXT &#xff0c;把下边的代码粘贴过去&#xff1a; DEL /F /A /Q \\?\%1 RD /S /Q \\?\%1 保存之后把名字改成&#xff1a;Delete.bat 然后把要删除的文件夹拖到这个文件上&#xff0c;即可删…