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

发布于 2024-07-07 06:26:05 字数 420 浏览 7 评论 0原文

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

我需要找到通过该图的最短路径,该路径从“开始”开始,在“结束”结束,并通过所有“必须通过”节点(以任何顺序)。

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

I have a undirected graph with about 100 nodes and about 200 edges. One node is labelled 'start', one is 'end', and there's about a dozen labelled 'mustpass'.

I need to find the shortest path through this graph that starts at 'start', ends at 'end', and passes through all of the 'mustpass' nodes (in any order).

( http://3e.org/local/maize-graph.png / http://3e.org/local/maize-graph.dot.txt is the graph in question - it represents a corn maze in Lancaster, PA)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

旧伤还要旧人安 2024-07-14 06:26:05

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

就您而言,考虑到您只有大约十二个标记为“必须通过”的,并且考虑到 12 个! 相当小(479001600),您可以简单地尝试仅“必须通过”节点的所有排列,并查看按该顺序访问“必须通过”节点的从“开始”到“结束”的最短路径 - 它将简单地是该列表中每两个连续节点之间的最短路径的串联。

换句话说,首先找到每对顶点之间的最短距离(您可以使用 Dijkstra 算法或其他算法,但对于这些小数字(100 个节点),即使是最简单的代码 Floyd-Warshall 算法 将及时运行)。 然后,一旦将其放入表中,请尝试“必须通过”节点的所有排列以及其余的排列。

像这样的东西:(

//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(n3),所有排列部分的运行时间为 O(k!n),并且100^3+(12!)(100) 实际上是微不足道的,除非你有一些真正的限制性约束。]

Everyone else comparing this to the Travelling Salesman Problem probably hasn't read your question carefully. In TSP, the objective is to find the shortest cycle that visits all the vertices (a Hamiltonian cycle) -- it corresponds to having every node labelled 'mustpass'.

In your case, given that you have only about a dozen labelled 'mustpass', and given that 12! is rather small (479001600), you can simply try all permutations of only the 'mustpass' nodes, and look at the shortest path from 'start' to 'end' that visits the 'mustpass' nodes in that order -- it will simply be the concatenation of the shortest paths between every two consecutive nodes in that list.

In other words, first find the shortest distance between each pair of vertices (you can use Dijkstra's algorithm or others, but with those small numbers (100 nodes), even the simplest-to-code Floyd-Warshall algorithm will run in time). Then, once you have this in a table, try all permutations of your 'mustpass' nodes, and the rest.

Something like this:

//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

(Of course that's not real code, and if you want the actual path you'll have to keep track of which permutation gives the shortest distance, and also what the all-pairs shortest paths are, but you get the idea.)

It will run in at most a few seconds on any reasonable language :)
[If you have n nodes and k 'mustpass' nodes, its running time is O(n3) for the Floyd-Warshall part, and O(k!n) for the all permutations part, and 100^3+(12!)(100) is practically peanuts unless you have some really restrictive constraints.]

行至春深 2024-07-14 06:26:05

运行 Djikstra 算法 查找所有关键节点(起点、终点和必须节点)之间的最短路径-pass),那么深度优先遍历应该告诉您通过生成的子图接触所有节点的最短路径 start ... Mustpasses ... end

run Djikstra's Algorithm to find the shortest paths between all of the critical nodes (start, end, and must-pass), then a depth-first traversal should tell you the shortest path through the resulting subgraph that touches all of the nodes start ... mustpasses ... end

就像说晚安 2024-07-14 06:26:05

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

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

不过,一旦有了这个新图表,您就完全遇到了旅行推销员问题(好吧,几乎......不需要返回到起点)。 上面提到的任何与此相关的帖子都将适用。

This is two problems... Steven Lowe pointed this out, but didn't give enough respect to the second half of the problem.

You should first discover the shortest paths between all of your critical nodes (start, end, mustpass). Once these paths are discovered, you can construct a simplified graph, where each edge in the new graph is a path from one critical node to another in the original graph. There are many pathfinding algorithms that you can use to find the shortest path here.

Once you have this new graph, though, you have exactly the Traveling Salesperson problem (well, almost... No need to return to your starting point). Any of the posts concerning this, mentioned above, will apply.

花之痕靓丽 2024-07-14 06:26:05

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

这样做的原因是,与旅行商问题不同,玉米迷宫不允许你直接从地图上的任何一点旅行到任何其他点,而不需要经过其他节点才能到达那里。

我实际上会推荐 A* 寻路作为一种值得考虑的技术。 您可以通过确定哪些节点可以直接访问哪些其他节点以及来自特定节点的每个跃点的“成本”来进行设置。 在这种情况下,看起来每个“跃点”的成本可能相同,因为您的节点看起来间隔相对较近。 A* 可以使用此信息找到任意两点之间的最低成本路径。 由于您需要从 A 点到达 B 点并访问其间大约 12 个点,因此即使使用寻路的强力方法也不会造成任何伤害。

只是考虑的替代方案。 它看起来确实非常像旅行推销员问题,这些都是值得阅读的好论文,但仔细观察,您会发现它只是让事情变得过于复杂。 ^_^ 这是来自以前处理过此类事情的视频游戏程序员的想法。

Actually, the problem you posted is similar to the traveling salesman, but I think closer to a simple pathfinding problem. Rather than needing to visit each and every node, you simply need to visit a particular set of nodes in the shortest time (distance) possible.

The reason for this is that, unlike the traveling salesman problem, a corn maze will not allow you to travel directly from any one point to any other point on the map without needing to pass through other nodes to get there.

I would actually recommend A* pathfinding as a technique to consider. You set this up by deciding which nodes have access to which other nodes directly, and what the "cost" of each hop from a particular node is. In this case, it looks like each "hop" could be of equal cost, since your nodes seem relatively closely spaced. A* can use this information to find the lowest cost path between any two points. Since you need to get from point A to point B and visit about 12 inbetween, even a brute force approach using pathfinding wouldn't hurt at all.

Just an alternative to consider. It does look remarkably like the traveling salesman problem, and those are good papers to read up on, but look closer and you'll see that its only overcomplicating things. ^_^ This coming from the mind of a video game programmer who's dealt with these kinds of things before.

忆梦 2024-07-14 06:26:05

这不是 TSP 问题,也不是 NP 难题,因为原始问题并不要求必须通过的节点仅被访问一次。 通过 Dijkstra 算法编译所有必须通过的节点之间的最短路径列表后,这使得答案变得非常非常简单,只需暴力破解即可。 可能有更好的方法,但一个简单的方法就是简单地向后处理二叉树。 想象一个节点列表 [start,a,b,c,end]。 将简单距离 [start->a->b->c->end] 相加,这就是您要超越的新目标距离。 现在尝试 [start->a->c->b->end] ,如果这样更好,请将其设置为目标(并记住它来自该节点模式)。 逆向排列:

  • [start->a->b->c->end]
  • [start->a->c->b->end]
  • [start->b ->a->c->end]
  • [开始->b->c->a->结束]
  • [开始->c->a->b->结束]
  • [start->c->b->a->end]

其中之一将是最短的。

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

This is not a TSP problem and not NP-hard because the original question does not require that must-pass nodes are visited only once. This makes the answer much, much simpler to just brute-force after compiling a list of shortest paths between all must-pass nodes via Dijkstra's algorithm. There may be a better way to go but a simple one would be to simply work a binary tree backwards. Imagine a list of nodes [start,a,b,c,end]. Sum the simple distances [start->a->b->c->end] this is your new target distance to beat. Now try [start->a->c->b->end] and if that's better set that as the target (and remember that it came from that pattern of nodes). Work backwards over the permutations:

  • [start->a->b->c->end]
  • [start->a->c->b->end]
  • [start->b->a->c->end]
  • [start->b->c->a->end]
  • [start->c->a->b->end]
  • [start->c->b->a->end]

One of those will be shortest.

(where are the 'visited multiple times' nodes, if any? They're just hidden in the shortest-path initialization step. The shortest path between a and b may contain c or even the end point. You don't need to care)

清醇 2024-07-14 06:26:05

Andrew Top 的想法是正确的:

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

我推荐 Lin-Kernighan 启发式:它是所有 NP 完全问题中最著名的启发式之一。 唯一要记住的另一件事是,在步骤 2 之后再次展开图形后,展开的路径中可能会有循环,因此您应该绕过这些循环(查看沿路径的顶点度数)。

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

Andrew Top has the right idea:

1) Djikstra's Algorithm
2) Some TSP heuristic.

I recommend the Lin-Kernighan heuristic: it's one of the best known for any NP Complete problem. The only other thing to remember is that after you expanded out the graph again after step 2, you may have loops in your expanded path, so you should go around short-circuiting those (look at the degree of vertices along your path).

I'm actually not sure how good this solution will be relative to the optimum. There are probably some pathological cases to do with short circuiting. After all, this problem looks a LOT like Steiner Tree: http://en.wikipedia.org/wiki/Steiner_tree and you definitely can't approximate Steiner Tree by just contracting your graph and running Kruskal's for example.

傾旎 2024-07-14 06:26:05

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

通常,这称为旅行商问题,并且无论您使用什么算法,都具有不确定的多项式运行时间。

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

Considering the amount of nodes and edges is relatively finite, you can probably calculate every possible path and take the shortest one.

Generally this known as the travelling salesman problem, and has a non-deterministic polynomial runtime, no matter what the algorithm you use.

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

孤君无依 2024-07-14 06:26:05

该问题涉及以任何顺序都必须通过。 我一直在尝试寻找有关必须通过节点的定义顺序的解决方案。 我找到了答案,但由于 StackOverflow 上没有任何问题有类似的问题,我在这里发布是为了让最多的人从中受益。

如果顺序或必须通过已定义,那么您可以多次运行 dijkstra 算法。 例如,假设您必须从 s 开始,依次经过 k1k2k3(按各自的顺序)并停在e。 然后你可以做的是在每对连续的节点之间运行 dijkstra 算法。 成本路径将由以下公式给出:

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

The question talks about must-pass in ANY order. I have been trying to search for a solution about the defined order of must-pass nodes. I found my answer but since no question on StackOverflow had a similar question I'm posting here to let maximum people benefit from it.

If the order or must-pass is defined then you could run dijkstra's algorithm multiple times. For instance let's assume you have to start from s pass through k1, k2 and k3 (in respective order) and stop at e. Then what you could do is run dijkstra's algorithm between each consecutive pair of nodes. The cost and path would be given by:

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

予囚 2024-07-14 06:26:05

对十几个“必须访问”的节点使用暴力怎么样? 您可以轻松地覆盖 12 个节点的所有可能组合,这为您提供了可以遵循的最佳电路来覆盖它们。

现在,您的问题被简化为找到从起始节点到电路的最佳路线之一,然后沿着该路线进行操作,直到覆盖它们,然后找到从该节点到终点的路线。

最终路径由以下部分组成:

start -> 电路路径* -> 必须访问节点的电路-> 通往终点的路径* -> 你找到

我用 * 标记的路径,如下所示

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

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

但它并没有接近寻找最佳解决方案,因为这可能涉及在搜索中留下必须访问的电路。

How about using brute force on the dozen 'must visit' nodes. You can cover all the possible combinations of 12 nodes easily enough, and this leaves you with an optimal circuit you can follow to cover them.

Now your problem is simplified to one of finding optimal routes from the start node to the circuit, which you then follow around until you've covered them, and then find the route from that to the end.

Final path is composed of :

start -> path to circuit* -> circuit of must visit nodes -> path to end* -> end

You find the paths I marked with * like this

Do an A* search from the start node to every point on the circuit
for each of these do an A* search from the next and previous node on the circuit to the end (because you can follow the circuit round in either direction)
What you end up with is a lot of search paths, and you can choose the one with the lowest cost.

There's lots of room for optimization by caching the searches, but I think this will generate good solutions.

It doesn't go anywhere near looking for an optimal solution though, because that could involve leaving the must visit circuit within the search.

安稳善良 2024-07-14 06:26:05

任何地方都没有提到的一件事是,同一顶点是否可以在路径中被多次访问。 这里的大多数答案都假设多次访问同一条边是可以的,但我对这个问题的看法(路径不应多次访问同一个顶点!)是不行访问同一个顶点两次。

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

One thing that is not mentioned anywhere, is whether it is ok for the same vertex to be visited more than once in the path. Most of the answers here assume that it's ok to visit the same edge multiple times, but my take given the question (a path should not visit the same vertex more than once!) is that it is not ok to visit the same vertex twice.

So a brute force approach would still apply, but you'd have to remove vertices already used when you attempt to calculate each subset of the path.

一页 2024-07-14 06:26:05

我尝试了模拟退火,branch&bound蚁群算法,所有这些算法都需要花费不真实的时间来解决即使是中等大小的图形。

因此,我为大图制定了快速响应的解决方案(gamedev 对于运行时计算可行)。

算法已在此处发布和记录(带动画):https://github.com/Stridemann/TravelShortPathFinder

如果您已经有一个图(并且在您的情况下您已经有了),只需跳过空间分割算法部分。

I tried simulated annealing, branch&bound, ant colony algorithms, all of them takes unreal amount of time to solve even medium size graphs.

So I made fast responding solution for a big graphs (gamedev viable for runtime calculation).

Algorithm is published and documented here(with animations): https://github.com/Stridemann/TravelShortPathFinder

If you already have a graph (and in your case you have), just skip the space segmentation algorithm part.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文