ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

A*搜索算法概述

2021-05-19 09:54:24  阅读:230  来源: 互联网

标签:goal 搜索算法 current 概述 启发式 节点 函数


A*搜索算法(A-star search algorithm)是一种常见且应用广泛的图搜索和寻径算法。A*搜索算法是通过使用启发式函数来指导寻路,从而高效的保证找到一条最优路径。A*搜索算法最初的设计是用来解决最短路径问题。但是,从理论来说A*可以解决大多数的成本代数问题。

A*搜索算法于1968年,由斯坦福研究院的Peter Hart,Nils Nilsson以及Bertram Raphael首次发表。

原理

A*搜索算法综合了最佳优先搜索算法(Best-first search)和Dijkstra算法的优点,通过一个成本估算函数来指导路径的搜索过程:$$ f(n) = g(n) + h(n) $$ 其中:

  • $n$:任意一个顶点

  • $g(n)$:表示起点到任意顶点$n$的实际成本

  • $h(n)$:是一种启发式函数,表示从任意顶点$n$到目标点的估算成本

在算法的每次迭代中,会从一个优先队列中取出$f(n)$值最小(估算成本最低)的节点作为下次待遍历的节点。这个优先队列通常称为open set。然后相应地更新其领域节点的$f(n)$和$g(n)$值,并将这些领域节点添加到优先队列中。最后把遍历过的节点放到一个集合中,称为close set。直到目标节点的$f(n)$值小于队列中的任何节点的$f(n)$值为止(或者说直到队列为空为止)。因为目标点的启发式函数$h(n)$值为0,所以说目标点的$f(n)$值就是最优路径的实际成本。

下面为A*搜索算法的主流程代码:

// heuristicFunction,启发式函数

function aStar(start, goal, heuristicFunction) {

  // 带估算的节点集合,为一个优先队列,每次取f(n)值最小的节点

  const openSet = new PriorityQueue()

  openSet.add(start) // 初始只有起点

  const closeSet = [] // 已被估算过的节点集合

  const gScore = { [start]: 0 } // g(n)值

  const hScore = { [start]: heuristicFunction(start, goal) } // h(n)值

  const fScore = { [start]: hScore[start] } // f(n)值

  const cameFrom = {} // 记录当前节点的上一个节点


  while(!openSet.isEmpty()) {

    const current = openSet.pollFirst()

    if(current === goal)

      return reconstructPath(cameFrom, goal) // 当前节点为目标点,返回最佳路径

    close_set.add(current)

    // neighborNodes,取出current节点的邻域节点

    for(let neighbor of neighborNodes(current)) {

      if(close_set.includes(neighbor))

        continue

      // 从起点到neighor的距离

      const tentativeGScore = gScore[current] + distance(neighbor, current)

      if(!openSet.includes(neighbor) || tentativeGScore < gScore[neighbor]) {

        // 记录neighbor节点的前一个节点

        cameFrom[neighbor] = current

        gScore[y] = tentativeGScore

        hScore[y] = heuristicFunction(neighbor, goal)

        fScore[y] = gScore[neighbor] + hScore[neighbor]

        openSet.add(neighbor)

      }

    }

  }

}


function reconstructPath(cameFrom, current) {

    const bestPath = [current]

    while(cameFrom[current]) {

      current = cameFrom[current]

      bestPath.unshift(current)

    }

    return bestPath

}

启发式函数

启发式函数作为A*搜索算法的核心,对算法的行为有着重大的影响,具体有以下几种情况:

  • 当启发式函数$h(n)$始终为0时,则将由从起点到任意顶点n的距离$g(n)$决定,此时A*算法将等效于Dijkstra算法。此时,可以考虑初始化一个值非常大的全局计数器C,每次处理一个节点时,将C分配给它所有领域节点。每次分配后,再将计数器C减一。节点被发现的越早,其$h(x)$值就越高。从而实现深度优先遍历。

  • 当$h(n)$始终小于等于顶点n到目标点的实际成本,则A*算法一定可以求出最优解。但是当$h(n)$的值越小,算法需要计算的节点越多,算法效率越低。

  • 当$h(n)$完全等于顶点n到目标点的实际成本,则A*算法将以较快的速度找到最优解。可惜的是,并非所有场景下都能做到这一点。因为在没有达到目标点之前,我们很难确切算出距离目标点还有多远。

  • 当$h(n)$大于顶点n到目标点的实际成本,则A*不能保证找到一条最短路径,但它运行得更快。

  • 当从起点到任意顶点n的实际成本$g(n)$等于0,或者$h(n)$值远远大于$g(n)$时,则只有h(n)起作用,此时算法演变成最佳优先搜索算法,速度最快,但可能得不出最优解。

可以看出,通过调节$h(n)$可以控制算法的精度和速度。一些情况下,我们未必要找到最佳路径,而是要高效的找出差不多好的路径,所以需要权衡,这个我们后面在讲。

二维网格地图中的启发式函数

在二维网格地图中,有如下几种常见的启发式函数:

  • 如果允许朝四个方向移动,即上下左右,则可以使用曼哈顿距离($L_1\,norm$)

  • 如果允许朝八个方向移动,即增加了对角线方向,则可以使用切比雪夫距离($L_\infty\,norm$)

  • 如果允许朝任何方向移动,则可以使用欧几里得距离($L_2\,norm$)


曼哈顿距离

曼哈顿距离(Manhattan distance)是指两点所形成的线段对坐标轴产生的投影的长度总和。在向量空间中又称为L1范数。在直角坐标系下,两点之间的曼哈顿距离为:$$ d(x, y) = |x_1 - x_2| + |y_1 - y_2| $$

图片

因此曼哈顿距离的启发式函数为:$$ h(n) = D \times (|n.x - goal.x| + |n.y - goal.y|) $$ 其中D是节点移动的单位成本,一般是一个常数。

切比雪夫距离

切比雪夫距离(Chebyshev distance)是指二个点之间的距离定义为其各座标数值差的最大值。在向量空间中又称为L∞范数。在直角坐标系下,两点之间的切比雪夫距离为:$$ d(x, y) = max(|x_1 - x_2|, |y_1 - y_2|) $$

图片

因此切比雪夫距离的启发式函数为:$$ h(n) = D \times max(|n.x - goal.x|, |n.y - goal.y|) $$ 上面的函数前提是直线和对角线的移动成本都是D,如果对角线的移动成本不是D,则上面的函数是不准确的,那就需要一个更准确的函数:$$ h(n) = D \times (|n.x - goal.x| + |n.y - goal.y|) + \sqrt{2} D min(|n.x - goal.x|, |n.y - goal.y|) $$

欧几里得距离

欧几里得距离(Euclidean distance)是指两点之间的直线距离。在向量空间中又称为L2范数。在直角坐标系下,两点之间的欧几里得距离为:$$ d(x,y)=\sqrt{(x_2 - x_1)^2+(y_2 - y_1)^2} $$

图片

因此欧几里得距离的启发式函数为:$$ h(n) = D \times \sqrt{(n.x - goal.x)^2+(n.y - goal.y)^2} $$

松弛

前面的章节我们提到,通过调节$h(n)$可以控制算法的速度和精度,一些情况下,我们可以牺牲最优性,来加快搜索速度。这时候需要做一些松弛操作,以便我们求得相较于最优解(1+ε)倍的次优解。

  • 静态加权。假设$h(n)$是一个启发式函数,我们可以用$h_w(n = ε \times h(n), ε > 1)$作为加权后的启发式函数,由于扩展了较少的节点,因此速度加快,找到的路径的误差最多是最小路径的ε倍。

  • 动态加权。通过动态改变权重大小,从而控制搜索的速度,可以使用如下的启发式函数:$$ f(n)=g(n)+(1+εω(n))h(n) $$ 其中

    • w(n)是计算权重的函数,当前搜索深度较小时,会获得较大的权重,加快搜索速度:$$ ω(n) = \begin{cases} 1 - \frac{d(n)}{N} & d(n) \leq N \ 0 & otherwise \end{cases} $$

    • $d(n)$是搜索深度,$N$是最终路径的预期长度,ε是允许的误差范围。

  • 通过偏好离目标点最近的节点来加快深度遍历,以提高速度。使用如下的启发式函数:

    $$  f\alpha(n)=(1+w\alpha(n))f(n) $$

    其中

    • wα(n)是计算权重的函数

      $$  w_\alpha(n)=\begin{cases} \lambda & g(\pi(n)) \leq g(\tilde{n}) \ \Lambda & otherwise \end{cases} $$

    • λ和Λ是$\lambda \leq \Lambda$的常量,$π(n)$是n的父节点,而ñ是要扩展的节点。


标签:goal,搜索算法,current,概述,启发式,节点,函数
来源: https://blog.51cto.com/u_15127660/2785151

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有