在图表中查找访问某些节点的最短路径

时间:2008-10-21 16:01:56

标签: algorithm graph-theory

我有一个无向图,大约有100个节点和大约200个边。一个节点标记为“开始”,一个节点标记为“结束”,并且大约有十几个标记为“必须通过”。

我需要找到通过此图表的最短路径,该路径从“开始”开始,以“结束”结束,并通过所有“必须”节点(按任意顺序)。 < / p>

http://3e.org/local/maize-graph.png / http://3e.org/local/maize-graph.dot.txt是有问题的图表 - 它代表宾夕法尼亚州兰开斯特的一个玉米迷宫)

11 个答案:

答案 0 :(得分:69)

其他人将此与旅行商问题进行比较可能没有仔细阅读您的问题。在TSP中,目标是找到访问所有顶点的最短周期(哈密顿循环) - 它对应于每个节点标记为'mustpass'。

在你的情况下,鉴于你只有十几个被标记为'mustpass',并且给出了12个!相当小(479001600),你可以简单地尝试只有'mustpass'节点的所有排列,并查看从'start'到'end'的最短路径,它按顺序访问'mustpass'节点 - 它只是是该列表中每两个连续节点之间最短路径的串联。

换句话说,首先找到每对顶点之间的最短距离(你可以使用Dijkstra算法或其他算法,但是使用那些小数字(100个节点),即使是最简单的代码Floyd-Warshall algorithm也会运行及时)。然后,一旦你在表中有这个,尝试你的'mustpass'节点的所有排列,其余的。

这样的事情:

//Precomputation: Find all pairs shortest paths, e.g. using Floyd-Warshall
n = number of nodes
for i=1 to n: for j=1 to n: d[i][j]=INF
for k=1 to n:
    for i=1 to n:
        for j=1 to n:
            d[i][j] = min(d[i][j], d[i][k] + d[k][j])
//That *really* gives the shortest distance between every pair of nodes! :-)

//Now try all permutations
shortest = INF
for each permutation a[1],a[2],...a[k] of the 'mustpass' nodes:
    shortest = min(shortest, d['start'][a[1]]+d[a[1]][a[2]]+...+d[a[k]]['end'])
print shortest

(当然这不是真正的代码,如果你想要实际路径,你必须跟踪哪个排列给出最短距离,以及所有对最短路径是什么,但你明白了。 )

任何合理的语言都会在最多几秒内运行:) [如果你有n个节点和k'必须'节点,它的运行时间对于Floyd-Warshall部分是O(n 3 ),对于所有排列部分是O(k!n),并且100 ^ 3 +(12!)(100)实际上是花生,除非你有一些非常严格的约束。]

答案 1 :(得分:23)

运行Djikstra's Algorithm以找到所有关键节点之间的最短路径(开始,结束和必须通过),然后深度优先遍历应该告诉您通过生成的子图的最短路径触及所有节点开始...必须...结束

答案 2 :(得分:14)

实际上,您发布的问题类似于旅行推销员,但我认为更接近于一个简单的寻路问题。您只需在尽可能短的时间(距离)内访问特定的节点集,而不需要访问每个节点。

原因在于,与旅行商问题不同,玉米迷宫不允许您直接从地图上的任何一个点移动到任何其他点,而无需通过其他节点到达那里。

我实际上建议将A *寻路作为一种技巧来考虑。您可以通过确定哪些节点可以直接访问哪些其他节点以及来自特定节点的每个跃点的“成本”来进行设置。在这种情况下,看起来每个“跳”可能具有相同的成本,因为您的节点看起来相对紧密。 A *可以使用此信息查找任意两点之间的最低成本路径。由于你需要从A点到达B点并且访问中间约12个,所以即使是使用寻路的蛮力方法也不会有任何伤害。

只是另类考虑。它确实看起来非常像旅行推销员的问题,这些都是很好的论文,但仔细观察,你会发现它只是过于复杂的事情。 ^ _ ^这来自视频游戏程序员的头脑,他之前处理过这类事情。

答案 3 :(得分:14)

这是两个问题...... Steven Lowe指出了这一点,但没有对问题的后半部分给予足够的尊重。

您应首先发现所有关键节点之间的最短路径(开始,结束,必须)。一旦发现了这些路径,您就可以构建一个简化的图形,其中新图形中的每个边缘都是原始图形中从一个关键节点到另一个关键节点的路径。您可以使用许多寻路算法来找到最短路径。

一旦你有了这个新的图表,你就会遇到旅行销售人员的问题(好吧,几乎......不需要回到你的起点)。上述任何与此相关的帖子都将适用。

答案 4 :(得分:5)

Andrew Top有正确的想法:

1)Djikstra的算法 2)一些TSP启发式。

我推荐Lin-Kernighan启发式:它是任何NP Complete问题中最着名的之一。唯一要记住的是,在第2步之后再次展开图形后,你可能在扩展路径中有循环,所以你应该绕过那些(看看你路径上的顶点的程度)。< / p>

我实际上不确定这个解决方案相对于最佳解决方案有多好。可能存在一些与短路有关的病理情况。毕竟,这个问题看起来很像Steiner Tree:http://en.wikipedia.org/wiki/Steiner_tree,你绝对不能通过收缩图表和运行Kruskal来近似Steiner Tree。

答案 5 :(得分:3)

考虑到节点和边缘的数量相对有限,您可以计算每个可能的路径并采用最短的路径。

通常这称为旅行商问题,并且无论您使用何种算法,都具有非确定性多项式运行时。

http://en.wikipedia.org/wiki/Traveling_salesman_problem

答案 6 :(得分:3)

是一个TSP问题,而不是NP-hard,因为原始问题不要求只访问一次必须通过的节点。在通过Dijkstra算法编译所有必须通过节点之间的最短路径列表之后,这使得答案变得更加简单,更加简单。可能有更好的方法,但简单的方法是简单地向后工作二叉树。想象一下节点列表[start,a,b,c,end]。求和简单距离[start-> a>&gt; b-&gt; c-> end]这是你要击败的新目标距离。现在尝试[start-&gt; a-&gt; c-&gt; b-&gt; end],如果更好地将其设置为目标(并记住它来自该节点模式)。在排列上倒退:

  • [开始 - &GT; A-&GT; B-&GT; C-&GT; end]的
  • [开始 - &GT; A-&GT; C-&GT; B-&GT; end]的
  • [开始 - &GT; B-&GT; A-&GT; C-&GT; end]的
  • [开始 - &GT; B-&GT; C-&GT; A-&GT; end]的
  • [开始 - &GT; C-&GT; A-&GT; B-&GT; end]的
  • [开始 - &GT; C-&GT; B-&GT; A-&GT; end]的

其中一个将是最短的。

(多次访问&#39;节点在哪里?如果有的话?它们只是隐藏在最短路径初始化步骤中.a和b之间的最短路径可能包含c甚至是终点。你不需要关心)

答案 7 :(得分:2)

告诉我们algorythm应该在大约一秒钟,一天,一周左右的时间运行会很好:) 如果一周没问题并且是一次性的,你可以在几个小时内编写一个软件并强制它。但如果它嵌入在用户界面中并且必须每天计算很多次......我认为是另一个问题。

答案 8 :(得分:1)

该问题讨论了必须以任何顺序通过。我一直在尝试搜索有关必须通过的节点的已定义顺序的解决方案。我找到了答案,但是由于关于StackOverflow的任何问题都没有类似的问题,因此我在此发布该信息,以使更多的人从中受益。

如果订单或必须通过的订单是定义的,则可以多次运行dijkstra的算法。例如,假设您必须从s开始,依次经过k1k2k3,然后在e处停止。然后,您可以做的是在每对连续的节点对之间运行dijkstra的算法。 费用路径将通过以下方式给出:

dijkstras(s, k1) + dijkstras(k1, k2) + dijkstras(k2, k3) + dijkstras(k3, 3)

答案 9 :(得分:0)

如何在十几个'必须访问'节点上使用暴力。您可以轻松地覆盖12个节点的所有可能组合,这样您就可以使用最佳电路来覆盖它们。

现在你的问题被简化为找到从起始节点到电路的最佳路线,然后你可以跟随它直到你覆盖它们,然后找到从那个到最后的路线。

最终路径由:

组成

开始 - &gt;电路路径* - &gt;必须访问节点的电路 - &gt;结束路径* - &gt;端

你找到我用*标记的路径

从起始节点到电路上的每个点进行A *搜索 对于每一个,从电路上的下一个和上一个节点到结尾进行A *搜索(因为你可以沿着任一方向的电路循环) 您最终得到的是很多搜索路径,您可以选择成本最低的路径。

通过缓存搜索有很多优化空间,但我认为这将产生良好的解决方案。

尽管如此,它并没有找到最佳解决方案,因为这可能涉及将必须访问电路留在搜索范围内。

答案 10 :(得分:0)

在任何地方都没有提到的一件事是,在路径中是否可以多次访问同一个顶点。这里的大多数答案都假设可以多次访问同一个边缘,但我的问题是给出了一个问题(路径不应该多次访问同一个顶点!)是它 ok两次访问同一个顶点。

因此,蛮力方法仍然适用,但是当您尝试计算路径的每个子集时,您必须删除已经使用过的顶点。