有效地均匀且独立地随机采样最短的


14

GG为图,令ssttG的两个顶点G。我们是否可以从st之间所有最短路径的集合中随机且均匀地有效采样最短ss - tt路径?为简单起见,我们可以假设G是简单的,无向的和未加权的。stG

即使在许多受限制的图中,sst之间最短路径的数量t也可以是G的大小的指数G。因此,我们自然希望避免实际计算所有最短的ss - tt路径。我不了解一般情况,但是在我看来,我们可以通过一些特殊的图类来实现这一点。

感觉就像有人必须考虑过的事情。是否对此进行了任何研究,或者即使对于一般图形,实际上也很容易做到吗?


好问题Juho。在考虑答案的同时,通过“随机均匀采样st路径”,您对什么有确切的了解?如果满足随机选择s和t的要求,那么这个问题就微不足道了,所以我想您的意思是,最短路径中的所有节点都以遵循均匀分布的频率(即概率)出现。还是还有其他定义?特别是对于二部图,您的问题似乎很简单,不是吗?
卡洛斯·利纳雷斯·洛佩斯

1
@CarlosLinaresLópez考虑说菱形图,说s在“垂直边缘”的右侧,而t在左侧。现在st之间有2条最短路径。该算法应以相等的概率返回这两个路径之一。因此st不是“随机挑选”的,而是作为输入给出的。这清楚吗?从这个意义上说,我不确定二部图是否真的很容易解决问题。ststst
Juho 2013年

1
@CarlosLinaresLópez换句话说,我们得到的曲线图G ^,和两个顶点小号V G ^ 。令Sst之间所有最短路径的集合。随机均匀地输出S的元素。Gs,tV(G)SstS
Juho 2013年

Answers:


6

我不是100%肯定这个答案是正确的,但是这里有:

我认为您可以在具有单个源和单个接收器的DAG中将其减少为从s t均匀地随机的任何路径。st

给定图GG

  1. 创建一个新的空白有向图,^ hH
  2. 第一:运行Dijkstra的最短路径的BFS部分,从开始小号,标记所有与他们的最短距离,从-节点小号ss
  3. d s v 为距s - v的最小距离; 从Dijkstra最短路径算法的BFS步骤可以知道这一点。d(s,v)sv
  4. 然后执行Dijkstra最短路径算法的下一步,获得最短路径,并将其存储在p中(从ts倒退)。pts
  5. 现在开始以下循环;注释中的扩展,以及以下内容:
    • q 0 = { t }q0={t}
    • q 0q0
      • q 1 = q1=
      • 对于ü q 0uq0
        • 所以我们想从t u中找到这个最短子路径的所有可能的下一个节点tu
        • 对于所有的边缘Û v ģ使得d 小号v < d 小号Û edge(u,v)Gd(s,v)<d(s,u)
          • v是相邻节点,其 d s 较小(将少 1个vd(s,)1
          • 因此,t u v是最短路径中的可能子路径。tuv
          • v H di-edge u v HvH,di-edge(u,v)H
          • 现在,我们需要在下一回合检查v的小邻居。v
          • v q 1vq1
      • q 0设置为q 1q0q1
        • q 0q 1q0q1

本质上,我正在收集可以在最短路径中使用的所有可能的节点,并将它们放在H中H

有关其工作原理的更多信息:

Dijkstra的最短路径算法通过先运行一个BFS和标记所有节点v 从他们的最短路径小号- v。下一步是去从后面牛逼- 小号,并按照最低邻近节点返回。vGsvts

关键是,在这里您可以选择任何最少相邻的节点。我在这里要做的是每一步都收集所有最不相邻的节点,这意味着我考虑了所有最短路径。

现在您迅速想到了,但是,嘿,为什么要对它们进行指数计算,但我却没有呢?

答案是,因为我使用一个集合来避免两次添加相同的节点,所以避免为每个可能的路径重新计算。

现在我们有了一个DAG,我们可以以任何方式从t - s遍历,并从s - t获得最短的反向路径。该图应将t作为唯一来源,将s作为唯一汇。tsstts


如果以上是正确的话,那么我认为我们可以进一步采取措施,并解决以下问题。

给DAG中的每个节点一个节点权重;节点权重将是从该节点到s的路径数。让我们称它为w v sw(v)

您可以快速计算它们,请参阅在G中找到从s到t的简单路径数的算法

一旦有了节点权重,就可以通过以下方式统一选择路径:

  • 将DAG布置为水平结构(用于可视化)
  • 在每个级别,请在节点之间选择任意顺序,即。“从左到右”的概念。
  • 向下遍历所述DAG:在每个步骤[ 1 | p | ](其中||表示最大路径的大小,在这种情况下,是最短路径的长度): ii[1,|p|]||
    • u i为当前节点(从t开始)uit
    • u i子节点的所有权重相加,然后使用RNG,在加权的子节点之间均匀地选择一个子节点v iuivi
    • 设置u i + 1 = vi i,然后进行下一步ui+1=vi

水平结构,我的初步尝试由左到右是部分的概念简单地生成[R [ 0 w ^ 牛逼,并选择一个路径的方式,但我并没有明白这一点,所以您可以放心地忽略它们。r[0,w(t))
Realz Slaw

1
这个答案看起来不错!我喜欢这个主意!我尝试以略有不同的方式(以我的回答)写出来,以测试我的理解。无论如何,我只是想分享我对这个可爱答案的赞赏!
DW

5

这是基于Realz Slaw答案中思想的解决方案。基本上,这是对他的思想的重新阐述,可能更清晰或更容易理解。该计划是我们将分两步进行:

  1. 首先,我们将构建的曲线图小号具有以下属性:从任何路径小号小号是从最短路径小号ģ,并从每一个最短路径小号ģ也存在于小号。因此,S恰好包含G中最短的路径:所有最短的路径,仅此而已。碰巧的是,S将是DAG。SstSstGstGSSGS

  2. 接下来,我们将来自所有路径均匀地随机采样小号小号stS

只要所有边都具有正权重,该方法就可以推广到任意有向图G,因此我将用这些术语来解释我的算法。令w u v 表示边u v的权重。(这将概括您给出的问题陈述。如果您有一个未加权的图,则只需假设每个边的权重为1。如果您有一个无向图,则将每个无向的边u v 视为两个有向边u vv 。)Gw(u,v)uv(u,v)uvvu


Step 1: extract SS. Run a single-source shortest-paths algorithm (e.g., Dijkstra's algorithm) on GG, starting from source ss. For each vertex vv in GG, let d(s,v)d(s,v) denote the distance from ss to vv.

Now define the graph SS as follows. It consists of every edge uvuv such that (1) uvuv is an edge in GG, and (2) d(s,v)=d(s,u)+w(u,v)d(s,v)=d(s,u)+w(u,v).

The graph SS has some convenient properties:

  • Every shortest path from ss to tt in GG exists as a path in SS: a shortest path s=v0,v1,v2,,vk=ts=v0,v1,v2,,vk=t in GG has the property that d(s,vi+1)=d(s,vi)+w(vi,vi+1)d(s,vi+1)=d(s,vi)+w(vi,vi+1), so the edge vivi+1vivi+1 is present in SS.

  • Every path in SS from ss to tt is a shortest path in GG. In particular, consider any path in SS from ss to tt, say s=v0,v1,v2,,vk=ts=v0,v1,v2,,vk=t. Its length is given by the sum of the weights of its edges, namely ki=1w(vi1,vi)ki=1w(vi1,vi), but by the definition of SS, this sum is ki=1(d(s,vi)d(s,vi1)ki=1(d(s,vi)d(s,vi1), which telescopes to d(s,t)d(s,s)=d(s,t)d(s,t)d(s,s)=d(s,t). Therefore, this path is a shortest path from ss to tt in GG.

  • Finally, the absence of zero-weight edges in GG implies that SS is a dag.

Step 2: sample a random path. Now we can throw away the weights on the edges in SS, and sample a random path from ss to tt in SS.

To help with this, we will do a precomputation to compute n(v)n(v) for each vertex vv in SS, where n(v)n(v) counts the number of distinct paths from vv to tt. This precomputation can be done in linear time by scanning the vertices of SS in topologically sorted order, using the following recurrence relation:

n(v)=wsucc(v)n(w)

n(v)=wsucc(v)n(w)

where succ(v)succ(v) denotes the successors of vv, i.e., succ(v)={w:vw is an edge in S}succ(v)={w:vw is an edge in S}, and where we have the base case n(t)=1n(t)=1.

Next, we use the n()n() annotation to sample a random path. We first visit node ss. Then, we randomly choose one of the successors of ss, with successor ww weighted by n(w)n(w). In other words:

choosesuccessor(v):
    n = 0
    for each w in succ(w):
        n = n + n(w)
    r = a random integer between 0 and n-1
    n = 0
    for each w in succ(w):
        n = n + n(w)
        if r < n:
            return w

To choose a random path, we repeatedly iterate this process: i.e., v0=sv0=s, and vi+1=vi+1= choosesuccessor(vi)(vi). The resulting path is the desired path, and it will be sampled uniformly at random from all shortest paths from ss to tt.

Hopefully this helps you understand Realz Slaw's solution more easily. All credit to Realz Slaw for the beautiful and clean solution to this problem!


The one case this doesn't handle is the case where some edges have weight 0 or negative weight. However, the problem is potentially not well-defined in that case, as you can have infinitely many shortest paths.


Glad you took the time to fully get my answer; I wasn't sure it is correct. Now I am vindicated :D.
Realz Slaw
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.