find访问特定节点的图中的最短path
我有一个无向图,有大约100个节点和大约200个边。 一个节点被标记为“开始”,一个是“结束”,并且大约有一打标记的“必须通过”。
我需要find通过这个图的最短path,从“开始”开始,结束于“结束”, 并通过所有“必须”节点(以任意顺序)。
( http://3e.org/local/maize-graph.png/ http://3e.org/local/maize-graph.dot.txt是图中的问题 – 它代表宾夕法尼亚州兰开斯特的玉米迷宫)
所有其他人比较这个旅游推销员问题可能没有仔细阅读你的问题。 在TSP中,目标是find访问所有顶点(哈密尔顿周期)的最短周期 – 它对应于每个节点标记为“mustpass”。
就你而言,鉴于你只有十几个标有“mustpass”的标签,并给出了12个! 是很小的(479001600),你可以简单地尝试只有“mustpass”节点的所有排列,然后查看从“start”到“end”的最短path,按顺序访问“mustpass”节点 – 它将简单地是该列表中每两个连续节点之间的最短path的连接。
换句话说,首先find每对顶点之间的最短距离(可以使用Dijkstraalgorithm或其他algorithm,但是使用这些小数字(100个节点),即使最简单的编码Floyd-Warshallalgorithm也能及时运行)。 然后,一旦你在桌子上有这个,尝试所有的“mustpass”节点的排列,其余的。
像这样的东西:
//Precomputation: Find all pairs shortest paths, eg 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
(当然,这不是真正的代码,如果你想要实际的path,你将不得不跟踪哪个排列给出了最短的距离,以及什么是最短path,但是你明白了。)
它将运行在任何合理的语言最多几秒钟:)
[如果有n个节点和k个'mustpass'节点,其Floyd-Warshall部分的运行时间为O(n 3 ),所有排列部分的运行时间为O(k!n),100 ^ 3 +(12! )(100)实际上是花生,除非你有一些非常限制的限制。]
运行Djikstra的algorithm来find所有关键节点之间的最短path(开始,结束和必须通过),然后深度优先遍历应该告诉你最短的path通过接触所有节点的结果子图开始。 mustpass …结束
其实,你发布的问题跟旅行商差不多,但我觉得更接近一个简单的寻路问题。 不需要访问每个节点,只需要以最短的时间(距离)访问一组特定的节点。
其原因是,与旅行商问题不同的是,玉米迷宫将不允许您直接从地图上的任何一点到达地图上的任何其他地点,而无需通过其他节点到达那里。
实际上我会推荐A *寻路作为一种技术来考虑。 您可以通过确定哪些节点直接访问哪些其他节点以及特定节点每跳的“成本”来设置此值。 在这种情况下,看起来每个“跳”可能具有相同的成本,因为您的节点似乎距离比较近。 A *可以使用此信息来查找任意两点之间的最低成本path。 既然你需要从A点到B点,并且访问大约12个中间点,那么即使是使用寻路的powershell方法也不会有任何伤害。
只是一个替代考虑。 它看起来非常像旅行推销员的问题,这些都是很好的文章,但看起来更接近,你会看到它只是过于复杂的事情。 ^ _ ^这来自于一个处理这些事情的电子游戏程序员的思想。
这是两个问题…… Steven Lowe指出这一点,但对问题的后半部分没有给予足够的尊重。
您应该首先发现所有关键节点(开始,结束,必须通过)之间的最短path。 一旦发现这些path,就可以构造一个简化的图,其中新图中的每个边是从原始图中的一个关键节点到另一个关键节点的path。 有许多寻路algorithm可以用来在这里find最短的path。
一旦你有这个新的图表,但是,你有旅游销售人员的问题(好吧,几乎…没有必要回到你的出发点)。 上述任何与此有关的职位将适用。
考虑到节点和边的数量是相对有限的,你可以计算出所有可能的path,并且取最短path。
一般来说,这被称为旅行商问题,并且具有非确定性的多项式运行时间,而不pipe你使用什么algorithm。
Andrew Top有正确的想法:
1)Djikstraalgorithm2)一些TSP启发式algorithm。
我推荐Lin-Kernighan启发式:这是NP Complete问题中最为人所知的一种。 唯一需要记住的是,在步骤2之后再次展开graphics之后,可能会在展开的path中出现循环,因此应该绕过这些循环(查看沿着path的顶点的程度)。
实际上我不确定这个解决scheme相对于最优的方式有多好。 可能有一些病例与短路有关。 毕竟,这个问题看起来像斯坦纳树很多: http : //en.wikipedia.org/wiki/Steiner_tree ,你绝对不能通过简单的收缩你的图和运行克鲁斯卡尔例如斯坦纳树。
如果一个星期没有问题,而且是一次性的,那么你可以在几个小时内写出一个软件,然后蛮力。 但是,如果它embedded在一个用户界面中,并且必须每天计算很多次……我认为是另一个问题。
这不是一个TSP问题,也不是NP难题,因为原来的问题并不要求只能访问一次必须传递的节点。 通过Dijkstraalgorithm编译所有必须传递的节点之间的最短path列表后,这使得答案非常简单,仅仅是蛮力而已。 可能有更好的方法,但简单的方法是简单地向后操作二叉树。 想象一下节点列表[开始,a,b,c,结束]。 总结简单的距离[开始→a-> b-> c->结束]这是您新的目标击打距离。 现在尝试[开始 – > a-> c-> b->结束],如果最好将其设置为目标(并记住它来自该模式的节点)。 向后排列:
- [开始 – > A-> B-> C->端]
- [开始 – > A-> C-> B->端]
- [开始 – > B-> A-> C->端]
- [开始 – > B-> C-> A->端]
- [开始 – > C-> A-> B->端]
- [开始 – > C-> B-> A->端]
其中之一将是最短的。
(“多次访问”节点在哪里?),它们只是隐藏在最短path初始化步骤中,a和b之间的最短path可能包含c或甚至终点,不需要关心)
如何在十几个“必须访问”节点上使用暴力。 您可以很容易地覆盖所有可能的12个节点的组合,并为您提供一个可以遵循的最佳电路。
现在你的问题被简化为寻找从起始节点到电路的最佳路线,然后你可以沿着这条路线前进,直到你覆盖它们,然后find从那个到最后的路线。
最终path由以下部分组成:
开始 – >path到电路* – >必须访问节点的电路 – >结束path* – >结束
你find我标记的path*就像这样
从开始节点到电路上的每个点进行A *search,从电路的下一个节点和前一个节点进行A *search(因为您可以沿任一方向循环回路)最后是很多searchpath,你可以select成本最低的那个。
cachingsearch有很多优化的空间,但我认为这会产生很好的解决scheme。
尽pipe如此,它并没有去寻找最佳的解决scheme,因为这可能涉及将必须访问电路留在search范围内。
有一点在任何地方都没有提到,是否可以在path中多次访问同一个顶点。 这里的大部分答案都假定可以多次访问同一个边,但是我提出了一个问题(一个path不应该多次访问同一个顶点!)是两次访问同一个顶点是不好的 。
所以暴力方法仍然适用,但是当您尝试计算path的每个子集时,您必须删除已经使用的顶点。