我有定向加权图G =(V,E),可能有循环。
我正在尝试确定完成任务的最佳时间效率算法:t 找到源节点和目标节点之间G中的所有简单路径,此路径中边缘的总权重小于某个值(为方便起见,我们将此值表示为PATH_WEIGHT_LIMIT)
所有权重均为正数且可以浮动。
所以,我的函数的原型将是:
def find_paths(G, source, target, path_weight_limit)
结果路径可能重叠,很好。
与此处讨论的非常相似,例如:
但是我找不到一个算法来解决这个特别的任务,我在之上提出找到源和目标节点之间G(有向,加权,循环)中的所有简单路径,该路径中边的总权重小于某个值
我认为应该使用修改后的DFS算法,重点关注路径当前部分的权重。但我认为它没有效果,也许有更好的算法来解决这个问题。
答案 0 :(得分:1)
理论上,每个节点的权重都是1+,因此循环不会成为问题,因为随着路径的增长,权重会增加。但是......如果你的任何节点的成本/权重<= 0,那么你应该包括一个最大时间或深度,以便停止寻路。
如果你想要完美的路径,请使用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上找到的信息更多。我加入它的原因是因为它允许您派生更强大的寻路算法,例如从多个起始位置开始查找多个路径和目标,甚至管理执行时间。它将帮助您实现算法。
寻路的本质是这个算法:
- 以节目列表 open 开头(通常包含1个项目)
- 选择最有希望的 1 节点
- 如果节点是目标 2 ,请将其添加到列表目标
- 如果节点有效,则生成相邻 3 候选节点的列表(list cand )
- 对于每个候选人,如果它无效 4 ,请将其从列表 cand 中删除。然后,将列表 cand 添加到列表 open 。
- 从列表 open 中删除所选节点,并将其添加到列表 closed
- 在寻路 5
时重复步骤2 醇>注意:
[1]:这是大多数寻路算法不同的地方;他们以不同方式优先考虑节点。
- First In,First Out(最老)是最简单的算法;只需按照添加到列表 open 的顺序检查节点。通常看起来像BFS。
- First In,Last Out(最新)选择添加到列表中的最新节点。它看起来很像DFS,具体取决于您的节点生成器。
- BFS搜索的深度最小,通常不是最佳选择算法。
- DFS优先考虑深度最大的节点。它倾向于选择一条路径并继续向下走,即使它永远消失。
- 贪婪选择最低的成本/重量去。搜索可能会陷入高成本区域,最终会出现与完美解决方案相比成本非常高的路径。通常A *是速度和最佳性之间的更好折衷。
- Dijkstra选择总成本/重量最低的节点。它在很大的范围内非常慢,但如果你想要完美的解决方案,这是一个不错的选择。
- Best-first选择具有最低(估计)剩余成本的节点来达到目标。在许多情况下,估计是到目标的实际距离(欧几里德,曼哈顿等),但并不总是可以知道。
- A *是Dijkstra + Best-first。这两个启发式方法相互抵消,因此在开放区域,A *会快速移动,但不会卡住。 A *并不完美,但它比Dijkstra快得多。您可以加权启发式以使算法更贪婪或更优化,还可以添加其他成本函数,例如距离健康包,封面或敌方玩家。
- 当节点包含大量数据时,自定义启发式通常会发挥作用。这通常意味着你已经从寻路移动到状态空间搜索领域,比如预测对手将在国际象棋中进行的下一步动作。涉及资源管理的问题将使用自定义启发式方法来确定每个资源的优先级,以确定节点的权重。
[2]:有时目标节点不是单个位置。有时您可能希望找到任何节点,这些节点具有某个对象,如健康工具包,商店或易于杀死的敌方玩家。通过使用
goal()
函数检查节点,可以定义多个端点。[3]:候选节点不需要彼此相邻。你正在做的是使用函数
f(node) = list(nodes)
。在搜索游戏状态以获得玩家的黄金或健康时,您可以为JUMP,ATTACK,REST等操作创建节点。在某些情况下,您需要在<<>之前验证生成的节点列表 / em>你添加它们。[4]:无效节点通常只是列表 closed 中之前搜索过的节点。但是,它们可能是太远的节点,与墙壁碰撞的节点(非常常见),将玩家健康状况降低到0的节点等等。如果您决定不使用
node isn't in closed list
作为条件,那么允许算法回溯(可以创建无限循环)。[5]:您可以实现算法以在满足某些条件时停止。通常这被认为是找到了1个目标节点,但是你可以做到这一点!您可以在一定时间后停止寻路,这对于游戏引擎非常有用,因为您可能需要暂停以渲染帧并防止延迟。如果节点列表变得太大,您也可以停止搜索,从而保持较低的内存使用率。一旦你有一定数量的解决方案,你甚至可以停止。
这个布尔停止条件/函数允许您在路径查找器耗时太长,占用资源或陷入无限循环时中止路径寻找。在单个线程上,这通常意味着您不再需要路径查找器。对于游戏引擎,在线游戏客户端和GUI应用程序,我喜欢在第二个线程中运行路径查找器并在需要时将其唤醒。如果探路者没有足够快地找到路径,那么一个愚蠢的AI会做出快速决定,直到寻路结束。