本篇文章介绍 ~O(n2.032) 的无向无权图全源最短路 stretch 2 近似算法和 ~O(n94) 的组合算法,以及 ~O(n2.214(1/ϵ)O(1)logW) 的非负整数边权 stretch (2+ϵ) 近似算法。其中 (1/ϵ)O(1) 表示的是 O((1/ϵ)κ),κ 是一个常数,但是精确值不方便表示(取决于 ω 和 2 的距离)。
~O(n2.032) 算法的过程与 ~O(n2.2867) 无权图上的 surplus 2 近似 几乎相同,仅仅有两个改动。
剩下的算法过程完全沿用。
为什么 surplus 2 的算法可以用于 stretch 2 呢?因为对于长度 ≥2 的最短路,surplus 2 比 stretch 2 更强,而长度 <2 的最短路是不用算的。
原算法描述如下
将 δ(V×D1) 视作矩阵,则我们需要求出 δ(V×D1)⋆δ(D1×V),其中 ⋆ 表示 (min,+) 矩阵乘法。对一般的 (min,+) 矩阵乘,没有 O(n3−Ω(1)) 的做法,但是如果我们将节点序列按照图的任意一棵生成树的欧拉序排列(此时一个点会出现多次),则 δ(D1×V) 的上下相邻两个元素的差不超过 1,对于拥有这样特殊性质的 (min,+) 矩阵乘(其中一个矩阵为 Row/Column Bounded-difference),有 ~O(n(2+r+ω(r))/2) 的做法,其中 ω(r) 表示 n×nr 矩阵和 nr×n 矩阵做正常矩阵乘法的指数 [Chi-Duan-Xie-Zhang '22]。
算法 0 (无权图上的 surplus 2 近似 III [Deng-Kirkpatrick-Rong-V. Williams-Zhong '22])
对图 (V,E),令
-
d0≤d1≤…≤dk=n,k 待定。
-
Vi 是度数 ≥di 的所有点。
-
Di 是一个大小为 ~O(ndi) 的关键点集,使得每一个 Vi 中的点都与至少一个 Di 中的点相邻。
-
Ei 是 u∉Vi∨v∉Vi 的所有边 (u,v)。|Ei|≤ndi。
-
E∗ 是每个 Vi 中的点和任一 Di 中的点连接的边集。|E∗|=~O(n)。
用 BFS 在 (V,Ei∪E∗) 上计算 δi(Di−1×V),1≤i≤k,时间复杂度为 ~O(n2didi−1)。
对 (V,E0) 应用算法 1 得到 d′(u,v)。时间复杂度 O(n2d120)。
求出 minw′∈Di1≤i≤k{δi(u,w′)+δi(w′,v)},假设 d 的选取足够分散,使得 k=o(nε),则复杂度为
~O(n(2+(1−lognd0)+ω(1−lognd0))/2) 考虑将 d(u,v)=min⎛⎜⎝d′(u,v),minw′∈Di1≤i≤k{δi(u,w′)+δi(w′,v)}⎞⎟⎠ 作为答案。
使用类似上文的推导可知这是一个 surplus 2 近似。
时间复杂度为
~O(n2d120+n2(d1d0+d2d1+…+dk−1dk−2+ndk−1)+n(3−lognd0+ω(1−lognd0))/2) 可知 didi−1 都相等,令他们均为 2,则 k=O(logn),符合要求。剩下的便是解方程,令 d0=n1−r,则方程为 52−12r=(2+r+ω(r))/2,即 2r+ω(r)=3,ω(r) 是一个很复杂的函数,查表可以估计出 r≈0.427,最终复杂度 ~O(n2.2867)。
|
|
有两部分产生了复杂度,一部分是 base case 的 ~O(n52−12r),另一部分是 (min,+) 矩阵乘 ~O(n(2+r+ω(r))/2)。
现在,在 stretch 2 中,我们可以将第一部分改为 ~O(n52−r),第二部分改为 ~O(nω(r)),这样便得到了 ~O(n2.032)。
当我们得到了一个 ~O(nω/ϵ) 的 (1+ϵ) 近似的 (min,+) 矩阵乘算法时,我们可以令 ϵ=1/logn,这样复杂度为 ~O(nω),近似比为 2+1/logn。当 δ(u,v)<logn 时,因为要向下取整,这是一个 2 近似,当 δ(u,v)≥logn 时,我们可以用 ~O(n2) surplus logn 的做法。
这个做法相当简单,我们原先的 ~O(n7/3) surplus 2 的算法中会用 BFS 计算每一层的 δ(Di×V),时间复杂度为 O(m|Di|),但现在我们不再每次重新计算,而是直接复用上一轮的结果,这样每一轮的误差会累积。对 surplus 2(k−1) 近似我们可以分 k 层。
算法 1 (无权图上的 ~O(n2−1/km1/k) surplus 2(k−1) 近似)
对图 (V,E),令
-
di=(m/n)1−i/k(logn)i/k,1≤i≤k。
-
Vi 是度数 ≥di 的所有点。
-
Di 是一个大小为 ~O(ndi) 的关键点集,使得每一个 Vi 中的点都与至少一个 Di 中的点相邻。特别地,令 Dk=V。
-
Ei 是 u∉Vi∨v∉Vi 的所有边 (u,v)。|Ei|≤ndi。
-
E∗ 是每个 Vi 中的点和任一 Di 中的点连接的边集。|E∗|=~O(n)。
从 1 到 k 枚举 i,对每个 w′∈Di,在 (V,Ei−1∪E∗∪di−1({w′}×V)) 上跑 Dijkstra。
考虑将 dk(u,v) 作为答案。
使用归纳法。使用类似 ~O(n7/3) surplus 2 算法的推导可知,最短路上存在 w,使得 (w,w′)∈E∗,w′∈Di−1,因此 u→w′ 可以通过 di−1 到达,w′→w→v 可以通过 E∗∪Ei−1 到达。当 di−1 的误差为 e 时,di 的误差为 e+2。
由于 Dk=V,所有 dk(u,v) 的误差都不会超过 2(k−1)。因此这是一个 surplus 2(k−1) 近似。
复杂度为 ~O(nd1⋅m+∑ki=2ndi(ndi−1+n))=~O(kn2(m/n)1/k)=~O(n2−1/km1/k)。
|
我们在原算法的 logn 层每一层都运行一遍 1+1logn 近似 (min,+) 矩阵乘,最后运行一遍 ~O(n2) surplus logn 近似,将两个结果取较小值。
算法 2 (无向无权图上的 stretch 2 近似)
对图 (V,E),令
-
d0≤d1≤…≤dk=n,k 待定。
-
Vi 是度数 ≥di 的所有点。
-
Di 是一个大小为 ~O(ndi) 的关键点集,使得每一个 Vi 中的点都与至少一个 Di 中的点相邻。
-
Ei 是 u∉Vi∨v∉Vi 的所有边 (u,v)。|Ei|≤ndi。
-
E∗ 是每个 Vi 中的点和任一 Di 中的点连接的边集。|E∗|=~O(n)。
用 BFS 在 (V,Ei∪E∗) 上计算 δi(Di−1×V),1≤i≤k,时间复杂度为 ~O(n2didi−1)。
对 (V,E0) 应用 stretch 2 算法得到 d′(u,v)。时间复杂度 ~O(n3/2d0)。
对 (V,E) 应用 surplus logn 算法得到 d′′(u,v),时间复杂度 ~O(n2)。
求出 minw′∈Di1≤i≤k{δi(u,w′)+δi(w′,v)} 的近似值,假设 d 的选取足够分散,使得 k=o(nε),则复杂度为
~O(n(2+(1−lognd0)+ω(1−lognd0))/2) 考虑将 d(u,v)=min⎛⎜⎝d′(u,v),d′′(u,v),minw′∈Di1≤i≤k{δi(u,w′)+δi(w′,v)}⎞⎟⎠ 作为答案。
使用类似上文的推导可知这是一个 stretch 2 近似。
时间复杂度为
~O(n3/2d0+n2(d1d0+d2d1+…+dk−1dk−2+ndk−1)+nω(1−lognd0)) 可知 didi−1 都相等,令他们均为 2,则 k=O(logn),符合要求。剩下的便是解方程,令 d0=n1−r,则方程为 52−r=ω(r),查表可以估计出 r≈0.4682,最终复杂度 ~O(n2.032)。
|
如果我们想要一个组合算法,即不使用快速矩阵乘法的做法,那就是把 ~O(nω(r)) 改为 ~O(n2+r),也即使用暴力矩阵乘。这样得到的式子为 52−r=2+r,最终复杂度为 ~O(n9/4)。这相当于只应用了来自 ~O(m√n) 算法的加速。
对于 (2+ϵ) 近似,我们沿用 ~O(m√n) 的 2 近似算法。不过把 s∈Si 上的 Dijkstra 改为对 δ(Si×V) 的 (1+ϵ) 近似,这称为多源最短路 (Multi-Source Shortest Path) 近似.
(1+ϵ) MSSP 近似的做法和 (1+ϵ) 的 APSP 近似非常接近,因为 ω(r,1,1)=ω(1,r,1)。唯一不同的一点在于,当我们计算 AS×V×AiV×V(AX×Y 为集合 X 到 Y 的邻接矩阵) 时,我们不能使用快速幂计算 A2iV×V,而是只能每次 (AS×V×Ai−1V×V)×AV×V,因此复杂度会多一个 n,这是我们不想要的。
有一类问题指出我们可以在原图上加少量的边使得所有 (u,v) 都可以经过一条长度不超过 β 的路,使得其权值不超过 (1+ϵ) 倍的最短路。称这样的边集为 hopset。
定理 对任意带权无向图 G=(V,E),κ>1,存在一个时间复杂度为 ~O(mn1/κ) 算法计算 (1+ϵ,β)-hopset H,使得 β=(κϵ)O(κ),|H|=O(n1+1/κ)。
我们令每次矩阵乘法的分辨率 R=β/ln(1+ϵ),这样最终的近似比可以达到 (1+ϵ)。复杂度为 ~O(nω(r)(κ/ϵ)O(κ)logW+mn1/κ)。
如果 ω(r)=2,ϵ 是固定的常数,我们可以对不同的 n 选择不同的 κ,让 β=~O(1),比如 loglogn/logloglogn,这样 κκ≤logn,复杂度为 ~O(n2logW(1/ϵ)O(loglogn/logloglogn))。
否则,我们可以选择足够大的常数 κ,使得 O(mn1/κ)≤O(nω(r)),此时复杂度为 ~O(nω(r)(1/ϵ)O(1)logW)。
分两种情况讨论是因为我们也不知道是否有 ω=2,而对于后一种情况,为了保证复杂度是 nω(r) 级别的,ω(r) 越小时 κ 越大,ω=2 时我们没有办法做到 ~O(n2(1/ϵ)O(1)logW)。
就现在而言,我们假设 ω≠2。这样,在 APSP (2+ϵ) 近似中,当 |S|=nr 时,计算 S,ball、枚举 y 的复杂度为 ~O(mn1−r)=~O(n3−r),运行 Sk 的复杂度为 ~O(nω(r)(1/ϵ)O(1)logW)。平衡后的复杂度为 ~O(n2.214(1/ϵ)O(1)logW)。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!