【问题标题】:Find all simple path from node A to node B in direct weighted graph with the sum of weighs less a certain value?在直接加权图中找到从节点 A 到节点 B 的所有简单路径,且权重之和小于某个值?
【发布时间】:2016-04-21 09:41:37
【问题描述】:

我有一个有向加权图 G=(V,E),它可能有循环

我正在尝试确定完成任务的最佳时间效率算法:to 在 G 中找到源节点和目标节点之间的所有简单路径,且该路径中边的总权重小于某个值(为方便起见,我们将此值表示为 PATH_WEIGHT_LIMIT)

所有权重都是正数,可以浮动。

所以,我的函数原型将是:

def find_paths(G, source, target, path_weight_limit) 

结果路径可能重叠,没关系。

很像这里讨论的,例如:

  1. 在加权有向循环图中查找从 A 到 B 的不同路径 NUMBER 条的算法
  2. 在 UNDERICTED 图中找到所有简单路径(NP 问题)

但是我没有找到一种算法来解决我在上面提出的这个具体任务在源节点和目标节点之间找到 G 中的所有简单路径(有向、加权、循环),并且该路径中的边的总权重小于一定值

我认为应该使用修改后的 DFS 算法,重点关注当前路径部分的权重。但我认为它并不有效,也许有更好的算法来解决这个问题。

【问题讨论】:

  • 我认为简单的 DFS 或 BFS 就可以了。 Dijkstra 或 A* 中的启发式方法没有多大帮助,因为无论如何您都想找到所有符合条件的路径。只需确保 (a) 如果当前路径包含循环或太长,则中止当前路径,并且 (b) 将路径收集到列表中,而不是只返回第一个。
  • @tobias_k "太长" 你的意思是当前总重量 > path_weight_limit?
  • 对于一个 n 顶点有向图,在最坏的情况下,这可以返回所有可能的最多 n 个不同顶点的序列作为路径:考虑由 n 个顶点组成的输入,在两个方向上都有一个权重为 1 的边每对顶点之间,权重限制为n。

标签: algorithm graph graph-algorithm graph-theory path-finding


【解决方案1】:

回答您的问题

理论上,每个节点的权重都为 1+,因此循环不会成为问题,因为权重会随着路径的增长而增加。但是...如果您的任何节点的成本/权重

如果您想要完美的路径,请使用 Djikstra 算法。如果您不在乎完美,请使用 A*。创建候选节点列表时,在将它们添加到搜索列表之前对其进行验证。任何总权重高于最大值的节点都应从候选列表中删除。

所以是这样的:

Current node -> List of candidate nodes --(are they less?)-> List of next nodes
merge(list of search nodes, list of next nodes)

不要在找到目标节点时停止,而是将目标节点添加到列表中,并在完成寻路时创建路径。大多数寻路节点的实现如下所示:

Node
- Node previous
- depth, cost
- misc data (coordinates, hp, gold, terrain, entity)

跟踪路径非常简单:将目标节点添加到列表中,然后将previous 添加到列表until previous = null 中。列表就是你的路径。

寻路是一个非常强大的工具,但是你可以在网上找到的大多数算法和指南都是介绍,甚至我找到的最好的指南,Amit Patel 的A* Tutorial,也不是很深入。

很多寻路系统因为过于专业化而只能找到一条路径,所以我将算法进行了概括。下面,您将找到一份深入的寻路指南,其中包含的信息比您在 google 上找到的要多。我包含它的原因是因为它允许您派生更强大的寻路算法,例如查找多个路径和目标,从多个起始位置开始,甚至管理执行时间。它将帮助您实现算法。

深度寻路指南

制作新的寻路系统所需的一切

寻路的本质是这个算法:

  1. 从节点列表open 开始(通常包含 1 个项目)
  2. 选择最有希望的1节点
    • 如果节点是目标2,则将其添加到列表goal
    • 如果节点有效,则生成相邻3个候选节点列表(listcand
  3. 对于每个候选,如果无效4,将其从列表cand中删除。然后,将 listcand 添加到 listopen
  4. 从列表中删除选定节点open 并将其添加到列表closed
  5. 在寻路时重复步骤 25

注意事项:

[1]:这是大多数寻路算法的分歧点;它们对节点的优先级不同。

  • 先进先出(最旧)是最简单的算法;只需按照节点添加到列表open 的顺序检查节点。通常看起来像 BFS。
  • 先进后出(最新)选择添加到列表中的最新节点。根据您的节点生成器,它可能看起来很像 DFS。
  • BFS 搜索深度最小,通常不是最佳算法选择。
  • DFS 优先考虑具有最大深度的节点。它倾向于选择一条路并继续沿着它走,即使它永远走下去。
  • 贪婪选择最低成本/重量移动到。与完美解决方案相比,搜索可能会卡在成本高昂的区域并最终得到一条成本非常高的路径。通常 A* 是速度和最优性之间更好的折衷方案。
  • Dijkstra 选择具有最低 成本/重量的节点。大面积速度很慢,但如果你想要完美的解决方案,它是一个不错的选择。
  • 最佳优先选择具有最低(估计)剩余成本的节点以达到目标。在许多情况下,估算值是到目标的实际距离(欧几里得、曼哈顿等),但并不总是可以知道。
  • A* 是 Dijkstra + 最佳优先。这两种启发式相互抵消,因此在开放区域中,A* 快速移动,但不会卡住。 A* 并不完美,但比 Dijkstra 的要快得多。您可以加权启发式算法以使算法更贪婪或更优化,还可以添加其他成本函数,例如与医疗包、掩体或敌方玩家的距离。
  • 当您的节点包含大量数据时,自定义启发式通常会发挥作用。这通常意味着您已经从寻路转移到状态空间搜索领域,例如预测对手在国际象棋中的下一步行动。涉及资源管理的问题将使用自定义启发式方法来确定每个资源的优先级,以确定节点的权重。

[2]:有时目标节点不是单个位置。有时您可能希望找到具有特定对象的任何节点,例如健康包、商店或易于杀死的敌方玩家。通过使用goal() 函数检查节点,可以定义多个端点。

[3]:候选节点不需要彼此相邻。您正在做的是使用函数f(node) = list(nodes)。在搜索游戏状态以获取玩家的金币或生命值时,您可以为 JUMP、ATTACK、REST 等操作创建节点。在某些情况下,您需要验证生成的节点列表之前 你添加它们。

[4]:无效节点通常只是列表已关闭中之前搜索过的节点。但是,它们可能是太远的节点、与墙壁碰撞的节点(非常常见)、将玩家生命值降至 0 的节点等。如果您决定不使用 node isn't in closed list 作为条件,那么您的算法是允许回溯(这会造成无限循环)。

[5]:您可以实现算法在满足某些条件时停止。通常这被认为是找到了 1 个目标节点,但是你可以用它做很多事情!您可以在一定时间后停止寻路,这对于游戏引擎来说非常有用,因为您可能需要暂停以渲染帧并防止延迟。如果节点列表变得太大,您也可以停止搜索,从而保持较低的内存使用率。一旦你有一定数量的解决方案,你甚至可以停下来。

这个布尔停止条件/函数允许您在寻路器花费太长时间、占用资源或陷入无限循环时中止寻路。在单个线程上,这通常意味着您不再需要探路者。对于游戏引擎、在线游戏客户端和 GUI 应用程序,我喜欢在第二个线程中运行探路者,并在需要时将其唤醒。如果探路者没有足够快地找到路径,那么愚蠢的 AI 会快速做出决定,直到寻路完成。

【讨论】:

  • Amit Patel 的 A* 教程太棒了
  • @Andrei 我发现的最好的之一。自从我上次去之后,他已经改进了很多
  • A* 找到与 Dijkstra 算法相同的完美路径,只要启发式是一致的并且不会高估。主要区别在于 Dijkstra 算法不针对目标,因此它平等地探索所有方向,而 A* 探索更接近目标的节点而不是远离目标的方向。您可以将 A* 视为 Dijkstra 的 but reweighted
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-02-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-14
  • 2021-12-19
  • 1970-01-01
相关资源
最近更新 更多