全源最短路 (APSP) 近似。有两种近似
- stretch k. δ(u,v)≤d(u,v)≤k⋅δ(u,v).
- surplus t. δ(u,v)≤d(u,v)≤δ(u,v)+t.
其中,δ(u,v) 表示 u,v 间真实的最短路长度。
为什么我们只考虑 δ(u,v)≤d(u,v) 呢,因为在组合算法中,“近似”都指用更少的信息去做松弛,因此每一个近似的值都对应着一条真实的路径。而在包含了 (min,+) 矩阵乘近似等代数算法中,向上近似和向下近似都是可以的((min,+) 矩阵乘近似的一个做法是,降低分辨率,用取整后的值做决策以降低复杂度),这时近似的值不对应一条真实的最短路了,只是说,这样的代数算法也一定包含了组合的部分,向上近似是为了和算法的其他部分不等号方向一致。
先来考虑无权图上的 surplus 2 近似。2 代表着我们可以选择最短路上的某个邻居作为松弛点。一个朴素的想法是,用度数的大小进行“根号分治”,只将一小部分点作为松弛点。
考虑将图上一些点设为关键点,使得所有度数大的点与至少一个关键点相连,用这些关键点松弛这些度数大的点,而将度数大的点之间的边删去。
令图上每个点有 1k 的概率被选为关键点,点之间概率独立,则度数为 d 的点有 1−(1−1k)d 的概率与一个关键点相邻。当 k=dclogn 时,1−(1−1k)d≥1−1nc,因此
定理 1 给定度数 d,可以找到一个大小为 ~O(nd) 的集合 D,使得高概率所有度数 ≥d 的点都与至少一个 D 中的点相邻。
其中 ~O 表示 O 忽略 log。
于是我们可以对度数大的点用关键点松弛,度数小的点直接 BFS。
算法 1 (无权图上的 surplus 2 近似 I [Aingworth-Chekuri-Indyk-Motwani '96])
对图 (V,E),令
-
V1 是度数 ≥d 的所有点。
-
D1 是一个大小为 ~O(nd) 的关键点集,使得每一个 V1 中的点都与至少一个 D1 中的点相邻。
-
E1 是 u∉V1∨v∉V1 的所有边 (u,v)。|E2|≤nd。
对 u∈D1,v∈V,用 BFS 计算出所有 δ(u,v)。时间复杂度 O(m|D1|)=~O(nmd)。令 δ(D1×V) 表示边集 {(u,v,δ(u,v))}。
对 (V,E1∪δ(D1×V)) 跑 Dijkstra。时间复杂度 O(n(|E1|+n|D1|))=O(n2d)。
考虑将 Dijkstra 的结果作为答案。
- 若 u∈D1∨v∈D1,d(u,v)=δ(u,v)。
- 若 u,v 最短路不经过任何 V1 中的点,d(u,v)=δ(u,v)。
- 否则若经过了某个 w∈V1,其相邻的关键点 w′∈D1,则 d(u,v)≤δ(u,w′)+δ(w′,v)≤δ(u,w)+δ(w,v)+2=δ(u,v)+2。
因此这是一个 surplus 2 近似。
时间复杂度为 ~O(n2d+nmd),令 d=n−12m12,最终时间复杂度为 ~O(n32m12),一般图 m=O(n2) 时为 ~O(n52)。
|
在第三种情况中,由于此时并不知道关键点是否就在最短路上,精确的长度无法求出。
这个算法是可以被优化的。考虑最短路上 w 的一个相邻的关键点 w′,u→w→w′→w→v 也是一个合法的 surplus 2 近似。因此算法 1 的浪费之处在于用 BFS 求出了所有的 δ(D1×V)。我们并不需要 D1 到 V 的一般意义上的最短路,而可以强制所有的近似都采取这样的策略,那就只需要对最短路上的边和 w 与 w′ 的边跑 BFS 即可。但问题在于我们无法提前知道要保留的边是哪些。
一个折中的策略是对度数大小选取两个阈值,较大的阈值对应的关键点个数少,按照同算法 1 的方法处理。在考虑一条最短路时,先看其能否通过这些关键点松弛,如果不行再考虑较小的阈值对应的关键点,这样后者就不需要再在整个图上跑 BFS 了,因为此时最短路有了性质,只需要保留满足这个性质的边(即不经过大度数的点的那些边)。
算法 2 (无权图上的 surplus 2 近似 II [Dor-Halperin-Zwick '96])
对图 (V,E),令
-
d1≤d2.
-
V1 是度数 ≥d1 的所有点。
-
D1 是一个大小为 ~O(nd1) 的关键点集,使得每一个 V1 中的点都与至少一个 D1 中的点相邻。
-
V2 是度数 ≥d2 的所有点。
-
D2 是一个大小为 ~O(nd2) 的关键点集,使得每一个 V2 中的点都与至少一个 D2 中的点相邻。
-
E1 是 u∉V1∨v∉V1 的所有边 (u,v)。|E1|≤nd1。
-
E2 是 u∉V2∨v∉V2 的所有边 (u,v)。|E2|≤nd2。
-
E∗ 是每个 V2 中的点和任一 D2 中的点连接的边集。|E∗|=~O(n)。
用 BFS 计算 δ(D2×V)。时间复杂度 O(m|D2|)=~O(n3d2)。
用 BFS 在生成子图 (V,E2) 上计算 δ′(D1×V)。时间复杂度 O(|E2||D1|)=~O(n2d2d1)。
对 u∈V,在 (V,E1∪δ(D2×V)∪δ′({u}×D1)∪E∗) 上跑 Dijkstra。时间复杂度 ~O(n(nd1+n2d2+nd1+n))=~O(n2d1+n3d2)。
考虑将 Dijkstra 的结果作为答案。
- 若 u∈D2∨v∈D2,d(u,v)=δ(u,v)。
- 若 u,v 最短路不经过任何 V1 中的点,也即,最短路上的点度数都很小,d(u,v)=δ(u,v)。
- 否则,若最短路经过了某个 w∈V2(也即,最短路上有一个度数很大的点),其相邻的关键点 w′∈D2,则 d(u,v)≤δ(u,w′)+δ(w′,v)≤δ(u,w)+δ(w,v)+2=δ(u,v)+2。
- 否则,若最短路不经过任何 V2 中的点,但是经过了某个 w∈V1(也即,所有点的度数都不大,但是存在度数中等的点),其相邻的关键点 w′∈D1。令 w 是最短路上最后一个这样的点,w′ 是那个 (w,w′)∈E∗ 的关键点。此时 u→w 不经过任何 V2 中的点,因此所有边 ∈E2;(w,w′),(w′,w)∈E∗;w→v(除了 w,v 自己)不经过任何 V1 中的点,因此所有边 ∈E1。所以 u→w→w′→w→v 是一条被包含的路径,d(u,v)≤δ(u,v)+2。
因此这是一个 surplus 2 近似。
时间复杂度为 ~O(n2d2d1+n2d1+n3d2)=~O(n2(d1+d2d1+nd2))。后三者乘积为 n,令 d1=n13,d2=n23,最终时间复杂度为 ~O(n73)。
|
我们也可以把算法 2 看作仅考虑一个阈值 d2,将 G′=(V,E2) 直接应用算法 1,这样的总复杂度为 ~O(n3d2+n32(nd2)12),令 d2=n23,也可以得到 ~O(n73) 的结果。
注意在 Dijkstra 时,我们对每个 u 包含了 δ′({u}×D1)。在第四种情况中,只有当我们选了最后一个这样的 w,以及在 Dijkstra 中包含了 E1,才可以让仅包含来自起点的 δ′({u}×D1),否则来自终点的 δ′({v}×D1) 也需要包含进去。两者的复杂度是有天差地别的。如果只包含一侧,便可以用单源最短路径每次只加 O(nd1) 条边,如果包含两侧,就只能把所有边 δ′(D1×V) 同时都加进去,这样将失去任何复杂度优势。
这个算法使用了两个阈值,将度数分为三层。立刻产生的想法便是可不可以通过分更多层来得到更优秀的复杂度,比如分 k 层就能得到诸如 O(poly(k)n2+1k) 的算法。答案是否定的。因为在最短路所有点不经过 Vi 但经过了 Vi−1 时,如果我们选取最后一个 Vi−1 中的点,那么 w→v 的所有边 ∈Ei−1,因此 Dijkstra 中要加入 Ei−1,但是这样最终复杂度就会有一项 ~O(n2dk−2)。因此这个做法下分更多层没有意义。
换句话说,这个算法的优化点就在于,考虑 δ(u,w)+δ(w,v) 时,起点侧 δ(u,w) 只需要加很少的边。
但是,与组合算法不同的是,还有另一类算法,直接专注于优化 minw{δ(u,w)+δ(w,v)} 这个计算过程本身。前面我们把所有需要的边并在一起算 Dijkstra 是为了方便,现在我们来仔细考量一下不同的边之间的需求关系。
对于四种情况
- u∈D2∨v∈D2,通过 δ(D2×V) 得到。
- 最短路不经过 V1 中的点,通过 (V,E1) 上的 Dijkstra 得到。
- 最短路经过了某个 w∈V2,通过 minw′∈D2{δ(u,w′)+δ(w′,v)} 得到。
- 最短路不经过 V2 中的点,但是经过了某个 w∈V1,通过 minw′∈D1{δ(u,w′)+1+δ(V,E1)(w,v)} 得到。
也就是说,在 Dijkstra 中,最短路实际上只有两步。
将 δ(V×D2) 视作矩阵,则我们需要求出 δ(V×D2)⋆δ(D2×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]。
由于我们现在只做 (min,+),第四种情况不能直接处理。因此我们进行分层,将每一层之间的 gap 缩小,做 logn 次 (min,+)。
算法 3 (无权图上的 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)。
|
如果不使用快速矩阵乘,直接用朴素的 ~O(n3−lognd0) 来做,最终复杂度与算法 2 相同。
最后,一个额外的事情是,我们可以将上面几个算法去随机化。上述算法均基于定理 1,考虑用别的方法替换掉它。考虑对一个点 v,如果将其作为关键点,则 v 和 v 的邻居都被覆盖了。我们总是从没有被覆盖的点中再考虑一个作为关键点,也即每次将 v 和 v 的邻居删去,考虑剩下的图。如果限制从度数大的开始删,那么当删去了 B 个点,剩下的图所有点中最大的度数为 d,则 Bd≤n,d≤nB,因此剩下的图的边数只有 O(n2B) 条。这是符合我们的要求的,因为上述几个算法对关键点的要求均为:关键点的数量是 O(nd) 时,剩余的边的数量是 O(nd)。所以上面几个算法均可以是确定性的,~O 也均可以改为 O。
对于有向图,由于上述算法均有 w→w′→w 的情节,它们无法再适用。
对于有权图,相邻并不意味着距离相近,因此 surplus 并不合理。现在尝试考虑 stretch。
先来考虑怎么寻找一个点 s 的前 b 相近的点,即最短路长度最近的 b 个点。如果直接使用 Dijkstra,复杂度是 ~O(nb) 的,不过我们可以限制 Dijkstra 的运行轮数与队列长度做到 ~O(b2)。
对 stretch 近似,一个最朴素的想法是这样的:我们还是沿用寻找关键点 w,用 d(u,v)=δ(u,w)+δ(w,v) 来近似 δ(u,v) 的方法。此时若有 δ(u,w)≤δ(u,v),则 δ(w,v)≤δ(w,u)+δ(u,v)≤2δ(u,v),d(u,v)=δ(u,w)+δ(w,v)≤3δ(u,v),因此得到一个很粗糙的上界 stretch 3。
算法 4 (带权图上的 stretch 3 近似)
对图 (V,E),令
对每个 D 跑完整的 Dijkstra,得到 δ(D×V)。
对于 u∈D∨v∈D∨v∈ball(u),d(u,v)=δ(u,v)。
否则,考虑 w∈ball(u)∩D,d(u,v)=δ(u,w)+δ(w,v)。
这是一个 stretch 3 近似。
时间复杂度为 ~O(nb2+n3b),令 b=n23,最终时间复杂度为 ~O(n73)。
|
这是一个广为人知 (folklore) 的算法。更优秀地,stretch 2 有 ~O(n32m12) 的做法,stretch 73 有 ~O(n73) 的做法,stretch 3 有 ~O(n2) 的做法。[Cohen-Zwick '97]
如果考虑空间复杂度的优化,我们所做的便是把规模 n×n 的最短路表 δ(V×V) 化简为一个空间更小的数据结构,使其能快速查询两点间的(近似)最短路。在上面的算法中为 O(n53) 空间 − O(1) 查询,如果不考虑时间复杂度的最优,令 b=n12,能做到 O(n32)−O(1)。对 stretch 2k−1 有 O(kn1+1k)−O(k) 的做法 [Thorup, Zwick '01]。
如果我们在无权图上做 stretch,我们可以非常轻松地给出一个 ~O(n2) 的 (2,1) 近似,其中 (a,b) 近似表示 d(u,v)≤aδ(u,v)+b。优化点在于我们不用像算法 3 那样计算出 minw′∈Di1≤i≤k{δi(u,w′)+δi(w′,v)},而实际上只需要用那个离 u 最近的关键点和那个离 v 最近的关键点作为松弛点,因为两者中恰好有一个没有一半的最短路长度。
算法 5 (无权图上的 (2,1) 近似)
对图 (V,E),令
-
di=2i,0≤i≤logn。
-
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)。
考虑将 d(u,v)=min1≤i≤k{δi(u,u′i)+δi(u′i,v),δi(u,v′i)+δi(v′i,v)} 作为答案,其中 u′i=argminw∈Diδi(u,w),v′i=argminw∈Diδi(w,v),也即只考虑一侧。
-
考虑 u,v 最短路上度数最大的点 x,令 i 满足 di−1≤deg(x)<di,则 x∈Vi−1,x∉Vi,因此最短路上的所有边 ∈Ei。设关键点 w∈Di−1 和 x 相邻,则 δi−1(u,u′)≤δ(u,x)+1,δi−1(v′,v)≤δ(x,v)+1。如果我们按照这样做,只会得到一个 (2,2) 近似,因为 x 有可能恰好处于最短路的中点,导致两边都取到上界。但是我们发现由于 Ei 的定义是 u∉Vi∨v∉Vi,我们可以找到度数第二大的点 x′,令 i′ 满足 di′−1≤deg(x′)<di′,那么最短路地所有边也 ∈Ei′。因此,
- 当 δ(u,v) 为偶数时,一定存在不为最短路中点的 x∗,满足,δ(u,x∗)≤12δ(u,v)−1,δi−1(u,u′)≤12δ(u,v),δi−1(u′,v)≤δi−1(u′,u)+δ(u,v),因此 δi−1(u,u′)+δi−1(u′,v)≤2δ(u,v)。
- 当 δ(u,v) 为奇数时,δ(u,x)≤12(δ(u,v)−1),δi−1(u,u′)≤12(δ(u,v)+1),δi−1(u′,v)≤δi−1(u′,u)+δ(u,v),因此 δi−1(u,u′)+δi−1(u′,v)≤2δ(u,v)+1。不难发现取到上界当且仅当 x,x′ 是最短路最中间的两个点。
因此这是一个 (2,1) 近似。
时间复杂度为 ~O(n2)。
|
对于 (2,0) 近似,有
续集
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?