All-Pair Almost Shortest Path(APASP)

问题描述

给定一个n个点m条边的无向无权重的图,找出所有点对之间的近似最短距离。

思路

最简单的方法就是从每个点开始跑BFS了。BFS的时间复杂度是O(m)的,那么总的时间复杂度就是O(nm)的。但是如果是稠密图,那O(m)=O(n2),总的时间复杂度就是O(n3)了。所以思路就是生成边数比较少的子图,然后加上一些构造出来的边来弥补那些被删掉的边造成的影响,再对得到的图跑最短路,来近似给出完整的图的结果。

随机选点

生成边数比较少的子图时,常用的方法是在图中随机选一些点,使得度数d的点大概率与某个选出来的点相邻。

假设每个点被选中的概率为1/k,那么一个度数为d的点的邻居中,有一个点被选中的概率就是1(11/k)d。可以证明只要随机选出O~(n/d)个点即可(我不会证明)。其中O~(n)表示O(n logc(n)),其中c为常数。

两层图:O~(n5/2)

V1为度数n1/2的点的集合。我们随机选出O~(n1/2)个点D1,使得所有V1中的点高概率与D1中的某个点相邻。

E2为满足uV1vV1的边(u, v)组成的边集。这样E2就相当于我们生成的边数比较少的子图了。由于其中的每条边的两个端点中,其中一方的度数必小于n1/2,也就是说,我们遍历所有的度数n1/2的点发出的边,即可遍历到所有的边,然后这样的点的个数最多是n,所以总的边数|E2|nn1/2=n3/2

我们在这个子图里对每个点跑BFS的话,对于点对(u, v),假如u和v以及它们的最短路径上所有的点的度数都小于n1/2,那么就可以求出来。但是如果路径上存在某个度数n1/2的点w(有可能是u或v本身),使得最短路径上某条边可能不在E2里,我们怎么估计出原图中u和v的最短路呢?

注意到w高概率与D1中的某个点w'相邻,所以考虑用w'为跳板,求出u到w'和w'到v的最短距离。记原图中u和v之间的最短距离为δ(u,v),那么δ(u,w)δ(u,w)+1δ(w,v)δ(w,v)+1,所以δ(u,v)=δ(u,w)+δ(w,v)δ(u,w)+δ(w,v)+2,即误差最大为2。

所以我们先在原图中对每个D1中的点跑一遍BFS,拿到D1×V里每个点对的最短距离,这个时间复杂度为O~(n1/2m)=O~(n5/2)。然后往E1中加入权重为δ(u,v)的从u到v的边,其中uD1vV,加入的这些边的集合记为δ(D1×V),得到的图则为E1δ(D1×V),其边数仍然为O(n3/2)。然后对得到的图的每个点跑dijkstra,复杂度O~(n5/2),就能得到误差最多为2的每个点对之间的最短路长度了。总的复杂度就是O~(n5/2)

一些想法:上面的描述中,使用E2时,我其实加上了u和v的度数都小于n1/2的假设,这样其实E2里只保留两个端点的度数都小于n1/2的边就好了,然而这样并不能减少E2里的边数,而且这样的话,对于相邻的一个度数小于n1/2,一个度数n1/2的点,本来可以都在E2里求出精确值,但是却非得以D1中的点为跳板,导致可能出现误差。

三层图:O~(n7/3)

三层图的结构即一个原图,一个2/3的子图,一个1/3的子图,而且由于同样只用了一个中继点,最大的误差仍然为2。

V1为度数n2/3的点构成的点集。
V2为度数n1/3的点构成的点集。
D1为随机选出的O~(n1/3)个点,这样V1里每个点都大概率与一个D1中的点相邻。
D2为随机选出的O~(n2/3)个点,这样V2里每个点都大概率与一个D2中的点相邻。
E2为满足uV1vV1的边(u, v)构成的边集。
E3为满足uV2vV2的边(u, v)构成的边集。
E为将每个V2中的点连接到一个D2中的点的边构成的集合。注意,每个V2中的点对应E中的一条边,不然最后的复杂度就不对了。

我们先从E3开始考虑。如果我们直接在E3上对每个点做BFS,那假如点对(u, v)之间的最短路上所有点(包括u和v)都不属于V2,那么求出来的就是精确值。但是,如果路径上存在属于V2的点,那么就可能有边不在E3中。这时我们考虑引入中继点来弥补。假设w是最后一个属于V2的点(可能是v),那么在D2中就大概率有一个点w'与w相邻。显然w到v的最短路径上的边都在E3里,因此w'到v在E3E上的最短路径长度最多比w到v的最短路径大1。现在问题转化为如何得到u到w'的无误差最短路长度,即每个D2×V中的点对的最短路径长度。

最简单的方法当然是直接在原图上对每个D2中的点跑BFS,但是这样的话复杂度太高了。

其实对于三层的图,有一个很美妙的性质:如果u到v的最短路上存在属于V1的点w,那么大概率存在一个D1中的点w',使得w'与w相邻,然后只要我们先对每个D1中的点跑BFS,得到边集δ(D1×V),然后只要图中加入这个边集,就能得到这种情况下误差最大为2的最短路长度,跟两层图同理。

这样,我们就只需要求解对于D2×V中的点对,两点之间的最短路径上所有点都V1时,最短路径是多少了。我们发现,这些边恰好都在E2中。所以我们在E2里对每个D2中的点跑BFS,得到δE2(D2×V)。算起点为u的最短路径时,只需要把δE2({u}×D2)加入到图中即可,不然复杂度不对。

所以总的流程如下:

  1. 在原图对每个D1中的点跑BFS,得到边集δ(D1×V),复杂度O~(n7/3)
  2. E2中对每个D2中的点跑BFS,得到边集δE2(D2×V),由于|D2|=O~(n2/3)|E2|=O(n5/3),所以复杂度O~(n7/3)
  3. 对每个点u,在E3Eδ(D1×V)δE2({u}×D2))中对每个点跑dijkstra。由于|E3|=O(n4/3)|E|=O(n)|δ(D1×V)|=O~(n4/3)|δE2({u}×D2))|=O~(n2/3),所以总的边数是O~(n4/3),复杂度是O~(n7/3)

所以最终的复杂度就是O~(n7/3)。一切都刚刚好,太妙了orz

推广到多层:O~(kn2+1/k),误差最大为2(k-1)。

分为k层,共k-1个中继节点,所以误差最大为2(k-1)。

Vi为度数n1i/k的点构成的集合。Vk就是所有点的集合V
Di为随机选出的O~(ni/k)个点,这样每个Vi中的点都大概率跟某个Di中的点相邻。Dk=V
Ei为满足uVi1vVi1的边(u, v)构成的边集。
Ei为将每个Vi中的点连接到一个Di中的点的边构成的集合。

思路:从i = 1到k,每次求出Di×V中每个点对的误差为2(i-1)的最短路径长度,算完到i = k后,就把V×V中每个点对的误差为2(k-1)的最短路径长度给算出来了,这就是我们要求的。

i = 1的情况,直接在原图上(即E1)对每个D1中的点做BFS即可。

i > 1的情况,假如我们在Ei中对每个Di中的点跑BFS,那么对于点对(u, v),假如最短路径上所有点都Vi1,那么求出的就是精确值,否则,设w为最后一个属于Vi1的点,那么大概率存在一个wDi1,使得w'与w相邻。从w'到v的最短路径长度可以在EiEi1中求出来。然后我们又已知Di1×V中每个点对的误差为2(i-2)的最短路径长度,假设u到Di1的最短路径长度构成的边集是δ({u}×Di1),那么对每个uDi,以u为起点,在EiEi1δ({u}×Di1)里跑dijkstra即可得到误差最多为2(i-1)的最短路径长度。

因为|Ei|=O(n2(i1)/k)|Ei1|=O(n)|δ({u}×Di1)|=O~(n(i1)/k),所以跑dijkstra的总边数为O~(n2(i1)/k),由于|Di|=O~(ni/k),所以复杂度为O~(n2+1/k)。跑k次,总复杂度kO~(n2+1/k)

我的想法:可不可以把三层图里直接把最顶层消掉的方法泛化到这里呢?少一个中继点就可以提高一些精度。

对稀疏图进行优化

注意到,前面的算法在分层的时候只用到了n,但是如果是稀疏图的话,其实高度数的节点是很少的,那其实很多层其实是白分了。所以对稀疏图的优化的思路就是在分层的时候,我们同时用到n和m。论文里给出的分层方法是把原先的n1i/k改成(m/n)1i/k

对k层图使用这个优化后,Vi为度数(m/n)1i/k的点构成的集合,|Di|=O~(n/(m/n)1i/k)=O~(n2i/k/m1i/k)|Ei|=O((m/n)1(i1)/kn)=O(m1(i1)/kn(i1)/k)|δ({u}×Di1)|=O~(n(i1)/k/m1(i1)/k)

我们假设O(m)O(n),那么|EiEi1δ({u}×Di1)|=O~(m1(i1)/kn(i1)/k),跑一层的复杂度是|Di||Ei|=O~(n21/km1/k),跑k层,复杂度就是O~(kn21/km1/k)。由于O(m)O(n2),所以这个复杂度永远不会比之前的kO~(n2+1/k)更差。

论文:<www.cs.tau.ac.il/~zwick/papers/apasp.ps.gz>

oooooooooooooooooooooooorz

posted @   寻找繁星  阅读(10)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示