【问题标题】:For every vertex in a graph, find all vertices within a distance d对于图中的每个顶点,找到距离 d 内的所有顶点
【发布时间】:2011-04-16 08:18:07
【问题描述】:

在我的特定情况下,该图表示为邻接列表,并且是无向且稀疏的,n 可以以百万计,d 为 3。计算 A^d(其中 A 是邻接矩阵)并挑选出非零条目有效,但我想要不涉及矩阵乘法的东西。对每个顶点进行广度优先搜索也是一种选择,但速度很慢。

【问题讨论】:

  • 由于该图表示为邻接列表,因此深度优先搜索(最多 d=3)应该可以工作。您不必对每个顶点都工作,而只需对可访问的顶点工作。
  • 你能解释一下为什么每个顶点的广度优先搜索很慢吗?我希望它是查找所需数据的最快方式。

标签: algorithm graph path shortest


【解决方案1】:
def find_d(graph, start, st, d=0):

    if d == 0:
        st.add(start)
    else:
        st.add(start)
        for edge in graph[start]:
            find_d(graph, edge, st, d-1)

    return st

graph = { 1 : [2, 3],
      2 : [1, 4, 5, 6],
      3 : [1, 4],
      4 : [2, 3, 5],
      5 : [2, 4, 6],
      6 : [2, 5]
    }

print find_d(graph, 1, set(), 2)

【讨论】:

    【解决方案2】:

    假设我们有一个函数verticesWithin(d,x),它可以找到距离d与顶点x之间的所有顶点。

    解决此类问题的一个好策略是公开缓存/记忆机会,提出以下问题:此问题的子问题如何相互关联?

    在这种情况下,如果d >= 1 是范围内所有ivertices(d-1,y[i]) 的并集,我们可以看到verticesWithin(d,x),其中y=verticesWithin(1,x)。如果是d == 0,那么它就是{x}。 (我假设一个顶点与自身的距离为 0。)

    在实践中,您需要查看d == 1 案例的邻接列表,而不是使用该关系,以避免无限循环。您还需要避免将x 本身视为y 的成员的冗余。

    另外,如果verticesWithin(d,x) 的返回类型从简单列表或集合更改为表示与x 之间距离增加的d 集合列表,则

    verticesWithin(d,x) = init(verticesWithin(d+1,x))

    其中init 是生成列表中除最后一个之外的所有元素的函数。显然,如果按字面翻译成代码,这将是一个非终止递归关系,因此您必须对如何实现它有点聪明。

    配备了这些子问题之间的关系,我们现在可以缓存verticesWithin 的结果,并使用这些缓存的结果来避免执行冗余遍历(尽管以执行一些集合操作为代价——我不完全确定这是一场胜利)。我将把它作为一个练习来填写实现细节。

    【讨论】:

      【解决方案3】:

      您已经提到了计算A^d 的选项,但这比您需要的要多得多(正如您已经说过的那样)。

      但是,有一种更便宜的方式来使用这个想法。假设您有一个(列)向量v,由 0 和 1 组成,表示一组顶点。向量w := A v 现在在每个节点上都有一个,可以从起始节点精确地一步到达。迭代,u := A w 对每个节点都有一个,你可以通过两个步骤从起始节点到达,等等。

      对于d=3,您可以执行以下操作(MATLAB 伪代码):

      v = j'th unit vector
      w = v
      for i = (1:d)
         v = A*v
         w = w + v
      end
      

      向量w 现在对于每个节点都有一个正条目,可以从jth 节点访问最多d 个步骤。

      【讨论】:

      • 我看不出这比矩阵乘法更便宜(时间方面),因为这个过程必须执行 n 次。无论如何,广度优先搜索对于单个起始顶点来说是一个更好的选择。也不需要您的矢量wA^d * v(以迭代或其他方式计算)具有与 w 相同的非零元素。
      • 你说得对,我以为你想从一个顶点开始。但是,请注意,这种方法没有矩阵-矩阵乘积,只有矩阵-向量乘积(计算成本要低得多)。
      【解决方案4】:

      在这种情况下,从给定顶点开始的广度优先搜索是最佳解决方案。您将找到距离 d 内的所有顶点,您甚至不会访问距离 >= d + 2 的任何顶点。

      这里是递归代码,但如果需要,可以使用队列轻松消除递归。

      // Returns a Set
      Set<Node> getNodesWithinDist(Node x, int d)
      {
        Set<Node> s = new HashSet<Node>();  // our return value
        if (d == 0) {
          s.add(x);
        } else {
          for (Node y: adjList(x)) {
             s.addAll(getNodesWithinDist(y,d-1);
          }
        }
        return s;
      }
      

      【讨论】:

      • 这是1个起始顶点的最佳解决方案,但问题是对每个顶点都这样做。
      猜你喜欢
      • 1970-01-01
      • 2014-07-29
      • 2015-07-04
      • 2012-10-31
      • 1970-01-01
      • 1970-01-01
      • 2019-07-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多