我有一个邻接列表形式的大型连接稀疏图。我想找到尽可能远的两个顶点,即diameter of the graph和实现它的两个顶点。
我对无向和定向案例中的这个问题感兴趣,对于不同的应用程序。在指导的情况下,我当然关心有向距离(从一个顶点到另一个顶点的最短有向路径)。
有没有比计算所有对最短路径更好的方法?
编辑:通过“尽可能远的距离”,我当然意味着“最长的最短路径” - 也就是说,从一个到最短距离的所有顶点对的最大值另一个。
答案 0 :(得分:16)
好吧,我已经对这个问题进行了一些思考,并且有点谷歌搜索,我很抱歉,但我找不到任何似乎不是“只找到所有对的算法”最短的路径“。
但是,如果你假设Floyd-Warshall是计算这类事物的唯一算法(| V | ^ 3的Big-Theta),那么我有一些好消息:Johnson的稀疏图算法(谢谢,值得信赖的CLRS!)计算(Big-Oh(| V | ^ 2 * lgV + VE))中的所有对最短路径,对于稀疏图,它应该渐近更快。
维基百科说它适用于有针对性(不确定无向,但至少我不能想到为什么不这样做),这里是link。
图表中还有其他可能有用的内容吗?如果它可以很容易地映射到2D平面上(因此,它的平面和边缘权重服从三角形不等式[它可能需要满足更严格的要求,我不确定])你可能能够打破一些几何算法(凸壳可以在nlogn中运行,从那里可以轻松找到最远的一对点。)
希望这有帮助! - Agor
编辑:我希望链接现在有效。如果没有,只需谷歌吧。 :)
答案 1 :(得分:6)
我不知道除了所有最短路径之外的更好的计算直径的方法,但Mathematica对PseudoDiameter使用以下近似值:
http://reference.wolfram.com/mathematica/GraphUtilities/ref/PseudoDiameter.html
答案 2 :(得分:4)
编辑我再次取消删除,只是这样我就可以继续评论了。我在这个答案下面对Johnson的算法有一些评论。 - 亚伦
我原来的评论: 我也很好奇这个问题,但没有答案。它似乎与Minimum Spanning Tree相关,即连接所有顶点但边缘最少(或权重最小)的子图。这是许多算法的老问题;其中一些似乎很容易实现。
我最初希望一旦发现MST,直径会很明显,但我现在失去了希望:-(也许MST可用于在直径上设置合理的上限,你可以使用加快搜索实际直径?
答案 3 :(得分:4)
这里有一些关于做出比无向图中所有对最短路径更好的想法,虽然我不确定它会有多大的改进。
这是一个子程序,它会发现两个节点相距D,如果有的话。选择一个任意节点x并计算M [x] =从x到任何其他节点的最大距离(使用任何单一源最短路径算法)。如果M [x]> = D,则x是我们的节点之一,而另一个很容易找到。但是,如果M [x] < D,那么我们正在寻找的端点都不会小于距离D的距离D-M [x](因为存在从该节点到所有其他节点的路径,通过x,距离 现在我们只需要设置D = diam(G)并运行上述程序。我们不知道diam(G)是什么,但我们可以得到相当紧的范围,对于任何x,M [x] <= diam(G)<= 2M [x]。选择几个x开始,为每个计算M [x],并计算diam(G)的上限和下限。然后我们可以在结果范围内进行二元搜索,使用上面的过程找到猜测长度的路径(如果有的话)。 当然,这只是无向的。我想你可以用有向图做类似的方案。坏节点是那些可以达到小于D-M [x]的x,而diam(G)的上限不起作用,所以你需要一个更大的二进制搜索范围。
答案 4 :(得分:2)
不确定它是否符合要求,但有趣:
HADI: Fast Diameter Estimation and Mining in Massive Graphs with Hadoop
Ú。 Kang,C。Tsourakakis,A。P. Appel,C。Faloutsos,J。Leskovec,“HADI:使用Hadoop进行大规模图表的快速直径估计和挖掘”,CMU ML技术报告CMU-ML-08-117,2008。
答案 5 :(得分:1)
如果图形是树(并且是无向的)。你可以简单地运行2个dfs。从随机节点u和dfs开始,找到最远的节点v。然后从v开始,找到最远的节点。这个长度是最佳的
答案 6 :(得分:0)
我真的怀疑是否有任何找到最长最短路径的方法而不必使用某种所有对最短路径算法(在最坏的情况下,重复找到单个源最短路径基本上完成所有对)。
如果图形不是树或DAG,则“直径”很难根据“最长路径”进行定义。如果图中有循环,则“最长”路径可以是无限的。因此,图的简单遍历不能产生所有节点上的最长路径。既然你已经声明你的图形不一定是非循环的,并且你对“最长的最短”路径感兴趣,似乎没有任何方法可以避免找到所有节点的最短路径。正如Agor所建议的那样,使用Johnson算法可能是最好的。您当然可以采用基于启发式的方法。使用pseudo-peripheral vertex的算法似乎是最常用的方法。
答案 7 :(得分:0)
请原谅我,如果我的答案在语法方面不正确,但我的算法课程是在一段时间之前(而不是英语)。
如果我正确理解您的问题,您想知道从节点A开始到达节点B而不“回溯”您的步骤,您可以计算的最高数字是多少。 如果是这种情况,我会将您的图形想象为非循环(循环选项稍后出现)。
首先,上限是边数。我如何看待事情:采取一个节点,创建一个树,其中节点位于根节点,您可以到达的每个后续节点位于下一级别。您构建的树的高度是直径,叶子是该距离的节点。如果那个距离=你完成的边数。如果没有,请选择另一个节点并重复。
我认为它类似于广度优先搜索的构建。对图表不了解,您可以使用一些启发式方法来查看哪个树方向(即应首先选择哪个节点)会更好,但这是另一个主题。
关于图的循环性 - 正如其他人指出的那些可以导致无限循环。摆脱这些的方法可能是“排除”属于一个循环的节点,并在它们之间添加最长的路径,作为通过进入循环并从其中出来获得的值,仅触摸每个节点一次。
现在,正如我所说,这种方法可以很容易地与做所有paires最短路径相同。最糟糕的案例复杂性当然是相同的,不可能是其他情况。
答案 8 :(得分:0)
获得此数字的估计值的一种方法是从随机点开始,并执行广度优先的“草火”算法,标记到每个节点的最短距离。这里最长的距离是你的估计。
使用不同的起点多次运行这种极快的算法,然后取最大值将提高估计的准确性,当然,给你一个不错的下限。根据图表的分布和连通性,这个估算甚至可能是准确的!
如果您的图表足够大,那么在您添加更多样本时估计值如何变化的渐近分析可能会让您投射到更好的猜测。
如果你对一个确切的答案感兴趣,那么除非你的图表很容易被分割成彼此弱连接的组件,否则你似乎不太可能远离偷工减料 - 在这种情况下你可以限制你的搜索不同组件中所有顶点对之间的最短路径。
答案 9 :(得分:0)
选择一个顶点v并执行BFS(v),这将计算所有顶点与v的距离。获得最长的距离。 这是O(V + E)
现在为所有v顶点运行此算法并选择这些最长距离的最大值。 总体复杂性:O(V *(V + E))
答案 10 :(得分:0)
您可能不必计算所有对,因为在无向图中有上限,并且可以向下驱动。
当从任意节点完成一次呼吸优先搜索(BFS)时,它可以产生按距离排序的所有其他节点的列表。当然,最长的距离是直径的下限,并且是直径的候选者。
相加的最远两个距离是您要查找的直径的上限。当使用前两个时,您可以排除已经对其进行BFS的任何节点,因为您已经知道使用这些节点作为端点的候选直径。通过优先级更高的距离节点成为执行BFS的下一个节点,上限将最终到达下限。在完成所有配对之前,可能会发生这种情况。
答案 11 :(得分:-2)
一种肮脏的方法:
我们知道对于具有| V | = n和| E | = m的图G(V,E),Dijkstra算法在O(m+nlogn)
中运行,这是针对单个源的。对于您的所有对问题,您需要为每个节点运行Dijkstra作为起点。
但是,如果您有许多机器,则可以轻松地并行此过程。
这种方法最容易实现,绝对不是很好。
答案 12 :(得分:-2)
是的,有更好的方法可以找到图表的直径。在这里,我做了一个简单的课程来演示它。顶点将是图表上的点。
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));
}
}