【问题标题】:Good algorithm for finding the diameter of a (sparse) graph?找到(稀疏)图直径的好算法?
【发布时间】:2010-11-14 12:02:57
【问题描述】:

我有一个大的、连接的、稀疏的邻接列表形式的图。我想找到两个尽可能远的顶点,即diameter of the graph和实现它的两个顶点。

对于不同的应用程序,我对无向和有向情况下的这个问题都感兴趣。在有向情况下,我当然关心有向距离(从一个顶点到另一个顶点的最短有向路径)。

有没有比计算全对最短路径更好的方法?

编辑:“越远越好”,我当然指的是“最长最短路径”——即所有顶点对中从一个到一个最短距离的最大值另一个。

【问题讨论】:

  • 好问题。你甚至在问之前做了一些阅读:)
  • 最好是无环图。
  • @dlamblin:我已经添加了一个澄清来解决你的观点。我的图表不是无环的,但这没关系。我正在寻找“最长的最短路径”。
  • 很酷的问题!你能和我们分享一下这个应用程序吗?
  • @nont,这里有几个例子:构建一个图,其中顶点是英语单词,边连接同义词。然后事实证明这个图有a very large component。相距最远但“同义”的两个词是什么?这可以是有向的或无向的,因为有些词库可以说不是“对称的”。另一个例子是让顶点是维基百科的文章并且有链接的有向边。实际上,您可能希望了解平面图上的两个点,例如相距很远的点。

标签: algorithm math graph-theory


【解决方案1】:

编辑我再次取消删除,只是为了继续评论。在这个答案下面,我有一些关于约翰逊算法的 cmets。 - 亚伦

我原来的评论: 我也很好奇这个问题,但没有答案。它似乎与Minimum Spanning Tree 有关,连接所有顶点但具有最少(或最低权重)边的子图。这是许多算法的老问题。其中一些似乎很容易实现。

我最初希望一旦找到 MST,直径就会很明显,但现在我失去了希望 :-( 也许 MST 可以用来为直径设置一个合理的上限,您可以使用它加快搜索实际直径的速度?

【讨论】:

  • 找到 MST 看起来是非常好的第一步,但我不能假设直径路径通过 MST。我可以想到一个简单的例子来说明这一点。可惜这里画不出来。
  • 确实如此。但是 MST 的直径不能小于整个图形的直径。因此,它在图的直径上设置了一个上限,而不是下限。但是,我必须承认,这样的上限可能不是很有用。
  • 顺便说一句,我是堆栈溢出的新手,我可能应该将我的原始评论作为“评论”而不是作为答案。我无意声称有答案,我只是想加入讨论。我看到两个用户( dlamblin 和 jrockway )已经成功地直接发布了 cmets,而不是答案;但我看不到这样的选择。道歉...
  • 感谢 A.Rex 的澄清。那我现在就删除我的答案,我希望这会再次增加问题的曝光率。我猜它也会删除其中一些 cmets :-(
  • @A. Rex:你的图表中是否有权重,其中是否有负数?约翰逊的算法(根据维基百科)只是转换数据以去除负权重,然后在每个节点上依次执行 Dijkstra 算法。因此,假设您的权重为非负(并且可能全部相等),看来您无论如何都必须做一些涉及 Dijkstra 算法的蛮力操作。
【解决方案2】:

好吧,我已经对这个问题进行了一些思考,并进行了一些谷歌搜索,很抱歉,但我找不到任何似乎不是“只需找到所有对”的算法最短路径”。

但是,如果您假设 Floyd-Warshall 是计算此类事物的唯一算法(|V|^3 的 Big-Theta),那么我有一个好消息要告诉您:Johnson's Algorithm for Sparse Graphs (谢谢你,值得信赖的 CLRS!)计算 (Big-Oh (|V|^2 * lgV + VE)) 中的所有对最短路径,这对于稀疏图应该是渐近更快的。

维基百科说它适用于定向(不确定非定向,但至少我想不出为什么不这样做的原因),这是link

关于图表还有什么可能有用的吗?如果它可以很容易地映射到 2D 平面上(因此,它的平面和边缘权重服从三角形不等式[它可能需要满足更严格的要求,我不确定])你也许能够打破一些几何算法(convex-hull 可以在 nlogn 中运行,从那里很容易找到最远的一对点)。

希望这会有所帮助! - 阿戈尔

编辑:我希望链接现在有效。如果没有,只需谷歌它。 :)

【讨论】:

  • 感谢 cmets。我知道约翰逊的算法,但我认为无论如何将它放在这里以供后代使用是个好主意。我的图表不能以任何方式自然地嵌入到低维空间中。
  • CLR(S) +1 !无向图只是所有边都加倍的有向图,每个方向都有一条!
【解决方案3】:

除了所有最短路径之外,我不知道计算直径的更好方法,但 Mathematica 使用 PseudoDiameter 的以下近似值:

  • 图形测地线是最短路径 图的两个顶点之间。这 图直径最长 所有图的可能长度 图的测地线。 PseudoDiameter 找到一个近似值 图形直径。它通过启动工作 从一个顶点 u,并找到一个顶点 v 那是离你最远的地方。这 通过将 v 视为重复该过程 新的起始顶点,并结束 当图形距离不再 增加。上一个顶点 具有最小的水平集 学位被选为最终 开始顶点 u,遍历是 做看看图形距离是否可以 被增加。这个图距离是 被认为是伪直径。

http://reference.wolfram.com/mathematica/GraphUtilities/ref/PseudoDiameter.html

【讨论】:

  • 谢谢!这绝对是一个似是而非的启发式方法。
  • 在无向非负权重的情况下,这个算法会找到图的实际直径吗?在有向的情况下,我可以想到会导致无法找到实际直径的示例,但对于无向的情况,我无法想象它们。我很想开始写代码。
  • @Bribles 对于定向案例,我想您必须在每个节点上进行两次搜索。一个向前(跟随链接 source -> dest)和一个向后(dest -> source),这样您就不会卡在没有输入/输出链接的节点中。那你就走更长的路。这是有向图的问题吗?我没有证据证明它的性能如何,但我想它会很好用。
  • @job 我真正的问题是对于无向图,如果伪直径实际上是真实直径而不仅仅是近似值?如果不是这样,上面列出的 PseudoDiameter 查找算法找不到真实直径的无向图示例是什么?
  • 如果我理解正确的算法,我相信我有一个反例:我们有节点A,B,C,D,E,F,G,H。邻接表:A:B,C ,E,G // B:A,C,D,G // C:A,B,D // D:B,​​C,F,G // E:A // F:D // G:A ,B,D,H // H:G 从C开始,最远的节点是H(距离3,CBGH) 从H出发,没有节点比3更远。(C,E,F并列)算法终止,但实际上最远的距离 4 在 E 和 F 之间,最短路径 EACDF。
【解决方案4】:

一个肮脏的方法:

我们知道,对于具有 |V|=n 和 |E|=m 的图 G(V,E),Dijkstra 算法在 O(m+nlogn) 中运行,这是针对单一来源的。对于您的全对问题,您需要为每个节点运行 Dijkstra 作为起点。

但是,如果您有很多机器,您可以轻松地并行此过程。

这种方法最容易实现,绝对不是很好。

【讨论】:

  • 关键问题是我是否能比计算所有对最短路径(无论是顺序还是并行)做得更好。
【解决方案5】:

这里有一些关于在无向图中比所有对最短路径做得更好的想法,尽管我不确定它会有多大的改进。

这是一个子程序,它将找到两个距离为 D 的节点,如果有的话。选择一个任意节点 x 并计算 M[x] = 从 x 到任何其他节点的最大距离(使用任何单源最短路径算法)。如果 M[x] >= D,那么 x 是我们的节点之一,另一个很容易找到。但是,如果 M[x]

现在我们只需要设置 D=diam(G) 并运行上述程序。我们不知道 diam(G) 是什么,但我们可以得到一个非常小的范围,对于任何 x,M[x]

当然,这只是无向的。我认为你可以用有向图做一个类似的方案。坏节点是那些可以在小于 D-M[x] 的时间内到达 x 的节点,并且 diam(G) 的上限不起作用,因此您需要更大的二分搜索范围。

【讨论】:

  • 谢谢。这个答案至少是有希望的,因为它提出了一种替代算法。我想知道性能是什么......
【解决方案6】:

我真的怀疑是否有任何方法可以找到最长最短路径而不必使用某种所有对最短路径算法(重复查找单源最短路径基本上是在最坏的情况下进行所有对)。

如果图不是树或 DAG,则很难根据“最长路径”来定义“直径”。如果图中存在循环,则“最长”路径可以是无限的。因此,图的简单遍历不能产生所有节点上的最长路径。由于您已经声明您的图不一定是非循环的,并且您对“最长最短”路径感兴趣,因此似乎没有任何方法可以避免找到所有节点的最短路径。正如 Agor 建议的那样,使用 Johnson 算法可能是最好的选择。

您当然可以采用基于启发式的方法。使用pseudo-peripheral vertex 的算法似乎是最常用的方法。

【讨论】:

  • 关于“如果图形不是树或 DAG,'直径' 的定义将变得毫无意义”:这不是真的。阅读 Wikipedia 链接以了解“直径”的标准定义,它不关心图形是否是非循环的。
  • 是的:你不能随心所欲地运行循环,只是为了增加路径的长度(边缘),因为那样它肯定不再是最短(加权)路径了.
  • @A.雷克斯:你是对的。我已经编辑了我的帖子以更正措辞。
【解决方案7】:

如果我的回答在语法上不正确,但我的算法课程是不久前的(而不是英语),请原谅我。

如果我正确理解您的问题,您想知道从节点 A 开始到节点 B 可以数到的最大数字是多少,而无需“追溯”您的步骤。 如果是这种情况,我会将您的图表想象为非循环的(循环选项稍后出现)。

首先,上限是边数。我的看法是:取一个节点,创建一棵树,该节点位于根节点,您可以到达的每个后续节点都位于下一个级别。您构建的树的高度是直径,叶子是该距离处的节点。如果那个距离=你完成的边数。如果没有,请选择另一个节点并重复。

我认为这类似于构建广度优先搜索。对图表了解不多,您可以使用一些启发式方法来查看哪个树方向(即应该首先选择哪个节点)会更好,但这是另一个主题。

关于图的循环——正如其他人指出的那样,这可能导致无限循环。摆脱这些的一种方法可能是“排除”属于一个循环的节点,并将它们之间的最长路径添加为通过进入循环并退出循环获得的值,每个节点只触摸一次.

现在,正如我所说,这种方法很容易与执行全对最短路径相同。最坏情况的复杂性当然是相同的,否则不可能。

【讨论】:

    【解决方案8】:

    获得这个数字的估计的一种方法是从一个随机点开始,并执行广度优先的“草火”算法,标记到每个节点的最短距离。这里的最长距离是您的估计。

    以不同的起点多次运行这个极快的算法,然后取最大值将提高估计的准确性,当然,还会给你一个不错的下限。根据图表的分布和连通性,这个估计甚至可能是准确的!

    如果您的图表足够大,对估计值如何随着您添加更多样本而发生变化的渐近分析可能会让您做出更好的猜测。

    如果您对确切的答案感兴趣,那么您似乎不太可能避免偷工减料,除非您的图表很容易划分为彼此弱连接的组件 - 在这种情况下,您可以限制您的搜索不同组件中所有顶点对之间的最短路径。

    【讨论】:

      【解决方案9】:

      不确定它是否符合要求,但很有趣:

      HADI: Fast Diameter Estimation and Mining in Massive Graphs with Hadoop

      U. Kang, C. Tsourakakis, A. P. Appel, C. Faloutsos, J. Leskovec,“HADI:使用 Hadoop 在海量图中进行快速直径估计和挖掘”,CMU ML 技术报告 CMU-ML-08-117,2008 年。

      【讨论】:

      • 看起来非常相关。谢谢!
      【解决方案10】:

      如果图是一棵树(并且是无向的)。您可以简单地运行 2 个 dfs。从随机节点 u 和 dfs 开始,找到最远的节点 v。然后从 v 开始,找到最远的长度。这个长度是最佳的

      【讨论】:

      • 感谢您的回答。就我而言,该图不像树那么稀疏。
      【解决方案11】:

      是的,有一种更好的方法可以找到图形的直径。这里我做了一个简单的类来演示一下。顶点将是您图表上的点。

      public class MyTestClass
      {
          //Simple Point struct
          struct Vertex
          {
              public float X, Y;
              public Vertex(float pX, float pY)
              {
                  X = pX;
                  Y = pY;
              }
          }
      
          //For getting the bounds of your graph
          struct BoundingBox
          {
              public float Left, Right, Bottom, Top;
              public BoundingBox(float pLeft, float pRight, float pBottom, float pTop)
              {
                  Left = pLeft;
                  Right = pRight;
                  Bottom = pBottom;
                  Top = pTop;
              }
          }
      
          //Properties
          Vertex[] vertices;
          BoundingBox bound;
          float diameter;
      
          //Constructor
          //Here is the fastest way to get the diameter >>
          public MyTestClass()
          {
              //Init objects
              vertices = new Vertex[100];
              for(int i = 0; i != vertices.Length; ++i) vertices[i] = new Vertex(i, i);
              bound = new BoundingBox(vertices[0].X, vertices[0].X, vertices[0].Y, vertices[0].Y);
              //Calculate BoundingBox
              for(int i = 0; i != vertices.Length; ++i)
              {
                  bound.Left = (vertices[i].X <= bound.Left) ? vertices[i].X:bound.Left;
                  bound.Right = (vertices[i].X >= bound.Right) ? vertices[i].X:bound.Right;
                  bound.Bottom = (vertices[i].Y <= bound.Bottom) ? vertices[i].Y:bound.Bottom;//NOTE: If Y is faces down, then flip bottom & top comparison
                  bound.Top = (vertices[i].Y >= bound.Top) ? vertices[i].Y:bound.Top;
              }
              //Messure Size of the BoundingBox
              float vecX = (bound.Right-bound.Left);
              float vecY = (bound.Top-bound.Bottom);
              diameter = (float)System.Math.Sqrt((vecX*vecX) + (vecY*vecY));
          }
      }
      

      【讨论】:

      • 感谢您的回答。我相信你误解了我的问题w.r.t。 “图”这个词。在这种情况下,图是由边连接的一组顶点。边缘可能是“加权的”,或者被假定为具有单位权重。然后,您正在通过此图表查看“路径”。特别是,顶点没有位置,比如在 x-y 平面上,唯一重要的是它们是如何连接的。再次感谢您的回答并保重。
      • 我知道我的答案是错误的。除非我仍然不明白,否则为您所做的事情找到直径的最快方法是找到每个点之间的最大点积,然后对最大值进行 sqrt 以获得直径。这是我对上面代码的想法的图像,即使它不是您想要的。 i1029.photobucket.com/albums/y354/zezba9000/GraphDiameter.png
      【解决方案12】:

      选择一个顶点 v 并执行 BFS(v),这将计算所有顶点到 v 的距离。获得最远的距离。 这是 O(V+E)

      现在对所有 v 个顶点运行此算法,并选择这些最长距离中的最大值。 总体复杂度:O(V*(V+E))

      【讨论】:

      • 我们实际上在课堂上被提出过这个问题,教授的答案实际上接近这个问题。根据定义,定义直径的两个顶点之一也是图中距离一半顶点最远的顶点。然后,如果您选择一个任意顶点并运行 Dijkstra(不是 BFS),那么最远的顶点就是您想要的顶点之一。然后从这个顶点再次运行 Dijkstra 以找到另一个。即使你必须运行 Dijkstra 两次,你仍然在 O(m+nlogn) 时间内运行。
      【解决方案13】:

      您可能不必计算所有对,因为在无向图中有一个可用的上限,并且可以向下驱动。

      当从任意节点执行呼吸优先搜索 (BFS) 时,它可以生成按距离排序的所有其他节点的列表。当然,最长距离是直径的下限,也是它的候选者。

      前两个距离相加是您寻找的直径的上限。选择前两个时,您可以排除已经完成 BFS 的任何节点,因为您已经知道使用这些节点作为端点的直径候选者。通过优先考虑距离较长的节点成为下一个执行 BFS 的节点,上限最终将达到下限。这可能发生在您完成所有配对之前。

      【讨论】:

        猜你喜欢
        • 2022-11-11
        • 2020-03-14
        • 1970-01-01
        • 2019-04-11
        • 2018-06-04
        • 1970-01-01
        • 1970-01-01
        • 2020-11-15
        • 1970-01-01
        相关资源
        最近更新 更多