Scratch 游戏传统导路算法

1 深度优先算法(DFS)

        这个算法不是最短路算法,只是一个基本的搜索算法,但是可以得出图中是否能找到指定节点

大致的思路:

  1. 从起始节点开始,将其标记为已访问。

  2. 检查当前节点的相邻节点中是否存在未访问的节点。

  3. 若存在未访问的节点,则选择其中一个未访问节点作为下一个当前节点,并将其标记为已访问。然后回到步骤 2。

  4. 若不存在未访问的节点,则回溯到上一个节点,即回到该节点的上一级节点,并重复步骤 2。

  5. 重复步骤 4,直到所有节点都被访问。


具体的实现

递归

        存在一个全局变量存储一个二维数组或邻接表,描述整张图

        建立一个函数dfs,参数标记当前节点在图中的位置,以及目标节点的特征或位置,返回值可以为是否找到

        进入函数,先将当前节点设为已访问

        如果当前节点是目标,直接返回真

        按照一定顺序读取周围可以到达的未访问过的节点,递归调用dfs函数,如果dfs返回真,则函数立即返回真,如果dfs返回伪,继续寻找周围可走的节点

        如果周围的节点都不能走,即函数执行到了这一里,则返回伪

  1. 依旧是存在一个全局变量存储一个二维数组或邻接表,描述整张图
  2. 建立一个栈s
  3. 将起点入栈,并且标记起点为已访问
  4. 只要栈中有元素循环:
  5. 获取栈顶元素为当前节点
  6. 弹掉栈顶元素
  7. 如果当前节点满足终点,就跳出循环
  8. 按照一定顺序读取周围可以到达的未访问过的节点,将其入栈并且标记为已访问

广度优先算法(BFS)

        虽然一般都是先学dfs再学bfs,但是我认为,bfs比dfs简单

思路是:

  1. 从起始节点开始,将其标记为已访问,并将其加入队列。

  2. 从队列中取出一个节点作为当前节点。

  3. 判断这个节点是否为终点,如果是,循环的次数就是最短路径长,且可以

  4. 检查当前节点的所有相邻节点中是否存在未访问的节点。

  5. 若存在未访问的节点,则将其标记为已访问,并将其加入队列。

  6. 重复步骤 2-5,直到队列为空。

它的实现差不多是这样,


Dijkstra 算法

        现在我们看一个早期的启发式搜索,它在图寻路中引用场景最广因为它考虑到边权,但是,边权为0时,它会退化为广度优先搜索

思路是:

  1. 初始化:将起始节点标记为已访问,距离起始节点的距离设置为0,并将所有其他节点的距离设置为无穷大。

  2. 找到最短路径:从起始节点开始,按照当前已知的最短距离,在未访问的节点中选择一个距离最小的节点进行访问。

  3. 更新节点距离:对于所选的节点,计算该节点与其相邻节点的距离和。如果通过当前节点到达相邻节点的距离比之前记录的最短距离要小,则更新最短距离。

  4. 标记已访问:标记当前节点为已访问,意味着已找到从起始节点到该节点的最短路径。

  5. 重复步骤2-4:重复执行步骤2到4,直到所有节点都被标记为已访问或者没有可访问的节点为止。这样,每个节点的最短路径就都得到了确定。

  6. 输出最短路径:最后,根据计算得到的最短路径信息,可以输出从起始节点到每个节点的具体路径

        实现也差不多,现在不方便贴代码,只能讲讲思路,一般实现是架邻接表而不是二维数组,因为二维数组不方便描绘边权


Floyd 算法

思路最简单的算法Floyd思路是:

  1. 初始化:创建一个二维数组D,用于保存节点之间的最短路径距离。初始时,如果两点之间存在边,则将其距离赋值给D;如果两点之间没有边,则将其距离设为无穷大。

  2. 迭代更新:对于每个顶点k,以顺序的方式遍历每对节点i和j,尝试通过节点k来改进节点i和节点j之间的最短路径。即,如果节点i到节点j的距离大于节点i到节点k的距离加上节点k到节点j的距离(即D[i][k] + D[k][j]),则更新节点i到节点j的距离为D[i][k] + D[k][j]。

  3. 重复迭代:继续进行迭代更新,每次都考虑一个新的中间节点k,直到所有节点都被作为中间节点考虑过一次。

  4. 输出最短路径:迭代完成后,二维数组D中保存的即为任意两点之间的最短路径距离。

但是,它的时间复杂度是难以接受的,高达O(n^3)


贪心搜索

        它在每一步选择时都采取当前看起来最好的选择,并希望通过一系列局部最优的选择来达到全局最优,且不会回头。

贪心搜索的基本思想如下:

  1. 初始化:确定问题的初始状态。

  2. 选择阶段:从当前状态开始,根据某个标准选择一个最好的操作或决策。这个选择是基于局部信息的,它不会考虑之前的选择或者未来的结果。

  3. 更新状态:根据所选择的操作或决策,更新当前状态。

  4. 终止条件:如果当前状态满足终止条件(到达终点),则结束搜索;否则返回第2步。

        具体实现依赖于一个启发函数,用于检测周围哪个点里终点更近

        贪心算法的目光是短浅的,所以得到的结果不一定正确,但是它通常执行速度较快。因此,在某些情况下,贪心搜索可能会得到次优解或者无法找到任何解。所以贪心这个算法是不合适的


最佳优先搜索

        最佳优先搜索(Best-First Search)是一种启发式搜索算法,它根据一个评估函数来选择下一个扩展的节点。评估函数用于估计当前节点到目标节点的代价或距离,算法总是选择具有最低评估值的节点进行扩展。

最佳优先搜索的基本思想如下:

  1. 初始化:确定问题的初始状态。

  2. 维护一个待扩展节点的优先队列,初始时将初始状态放入队列中。

  3. 重复执行以下步骤直到找到解决方案或队列为空:

a. 从队列中取出评估值最低的节点。

b. 检查当前节点是否为目标节点,如果是,则找到了解决方案,结束搜索。

c. 否则,根据当前节点扩展出所有可能的子节点,并计算每个子节点的评估值。

d. 将子节点按照评估值由低到高的顺序插入队列中。

        最佳优先搜索使用评估函数来选择下一个扩展的节点,根据评估值的大小进行排序。这种排序策略使得搜索算法更有可能朝着接近目标的方向前进,从而提高搜索效率。然而,最佳优先搜索并不能保证找到全局最优解,因为它只关注当前最佳的节点,并没有考虑之前的选择或未来的结果,但是最佳优先算法的比贪婪搜索更容易得到最优解,它有时会考虑回溯,但是依旧不保证最优解


A*

        A*(A-Star)是一种综合了最佳优先搜索和Dijkstra算法的启发式搜索算法。它在搜索过程中维护了两个值:一个是从起始节点到当前节点的实际代价(g值),另一个是从当前节点到目标节点的估计代价(h值)。

A*算法的基本思想如下:

  1. 初始化:确定问题的初始状态,将起始节点放入开放列表。

  2. 维护两个列表:开放列表:存储待扩展的节点,按照f值(f = g + h)进行排序。 关闭列表:存储已经扩展过的节点。

  3. 重复执行以下步骤直到找到解决方案或开放列表为空:

a. 从开放列表中选择f值最小的节点作为当前节点。

b. 检查当前节点是否为目标节点,如果是,则找到了解决方案,结束搜索。

c. 否则,将当前节点从开放列表移动到关闭列表。

d. 根据当前节点扩展出所有可能的子节点,并计算每个子节点的g值和h值。

e. 对于每个子节点,如果它已经在开放列表或关闭列表中,比较新的g值与已有的g值,选择较小的更新;否则,将子节点加入开放列表。

        A算法使用了启发式估计函数来指导搜索过程,它综合了实际代价和估计代价。g值表示从起始节点到当前节点的实际代价(通过已经扩展的路径),h值是从当前节点到目标节点的估计代价,通常使用启发式函数(如曼哈顿距离或欧几里得距离)来计算。A算法选择f值最小的节点进行扩展,f值较小的节点具有更高的优先级。

        A算法在满足一定条件下能够保证找到最短路径(最佳解决方案),即f值最小的节点是全局最优解。然而,在某些情况下,A算法可能会受到启发式函数的影响而无法找到最佳解决方案,或者搜索空间非常庞大时会消耗大量的时间和内存。


B*

        接下来对A*进行一些优化

        在A*算法中,h值是通过启发式函数进行估计的,这个估计值对搜索的效率和质量影响很大。然而,有些情况下,启发式函数可能无法提供准确的估计。例如,在某些问题中,启发式函数可能无法获得目标节点的准确估计代价,导致搜索算法受到偏差而无法找到最优解。

        B算法引入了一个新的概念——子目标(subgoal),并采用了更加灵活的估计方式。B算法中,该算法会先生成一个子目标,然后通过子目标来修正启发式函数的估计值。具体步骤如下:

  1. 初始化:确定问题的初始状态,将起始节点放入开放列表。

  2. 维护两个列表:开放列表:存储待扩展的节点,按照f值(f = g + h)进行排序。 关闭列表:存储已经扩展过的节点。

  3. 重复执行以下步骤直到找到解决方案或开放列表为空: a. 从开放列表中选择f值最小的节点作为当前节点。 b. 检查当前节点是否为目标节点,如果是,则找到了解决方案,结束搜索。 c. 否则,将当前节点从开放列表移动到关闭列表。 d. 根据当前节点扩展出所有可能的子节点,并计算每个子节点的g值和h值。 e. 对于每个子节点,根据子目标修正启发式函数的估计值,并计算新的f值。 f. 将子节点加入开放列表。

        B算法通过不断调整启发式函数的估计值来获取更准确的路径估计。它使用子目标来指导搜索过程,根据子目标的信息修正启发式函数的估计值,并根据新的估计值进行节点扩展。这个过程允许B算法在搜索过程中逐渐修正路径的估计,并更好地适应问题的特点。


D*

        D*(D-Star)是一种基于改变图搜索和路径规划的增量式算法。它用于在已知环境地图上进行路径规划,并能够在地图信息发生变化时进行快速更新。

D*算法的基本思想如下:

  1. 初始化:确定问题的初始状态,包括起始节点和目标节点,并初始化其他相关数据结构。

  2. 维护两个主要数据结构:状态图(State Graph):记录了每个节点的状态、代价、连接关系等信息。 优先级队列(Priority Queue):按照代价进行排序的队列,用于选择下一个扩展的节点。

  3. 重复执行以下步骤直到找到解决方案或无法找到路径: a. 从优先级队列中选择代价最小的节点作为当前节点。 b. 检查当前节点是否发生了变化(例如,代价更新或连接关系改变),如果是,则更新相关信息。 c. 更新与当前节点相邻的节点的代价,并计算新的代价值。 d. 如果目标节点的代价发生了变化,则重新评估路径。否则,沿最佳路径进行扩展。 e. 根据更新的信息,重新排序优先级队列。

        D算法通过不断更新节点的代价和连接关系来适应地图变化,并动态调整路径规划过程。它使用增量式的搜索和更新策略,可以在修改地图或代价信息后,快速重新规划路径。D算法重复进行节点的选择和扩展,直到达到目标节点或无法找到路径。


逆向与双向优化

        起点和终点对换一下,再执行算法就是逆向,然而,这没太大的优化作用,接下来结合正向和逆向

        的思路,将正向的搜索结果放入一个数组,逆向的放入另一个,直到两个数组出现重叠,说明找到,这样可以有效的缩减搜索范围实现优化

猜你喜欢

转载自blog.csdn.net/leyang0910/article/details/131946401