今天,还是国庆和中秋双节的时间节点,一个天气不错的日子,孩子已经早早的睡觉了,玩了一整天,也不睡觉,累的实在扛不住了,勉强洗澡结束,倒床即睡着的节奏。。。

不多说题外话,进入正题。

什么是A*搜索算法呢?就用百科的解说吧:

A*算法,A*(A-Star)算法是一种静态路网中求解最短路径最有效的直接搜索方法,也是解决许多搜索问题的有效算法。算法中的距离估算值与实际值越接近,最终搜索速度越快。

A*搜索的实际应用场景很多,但是大家最为熟悉的恐怕莫过于游戏了。典型的游戏就是穿越障碍寻宝,要求在最少的代价内找到宝贝,通常游戏中的代价,就是用最少的步骤实现宝贝的找寻。还有一种游戏场景是,给定入口地点,用最少的步骤走到指定的出口地点,中间有障碍物,每次只能在上下左右四个方向上走一步,且不能穿越障碍物。

就拿第二种游戏场景来解释A* search具体指的是什么内容吧。

如上图所示,假设我们有一个7*5的迷宫方格,绿色的点表示起点,红色的点表示终点。中间三个蓝色的格子表示一堵墙,是障碍物。游戏的规则,是绿色的起点,每次只能向上下左右4个方向中移动一步,且不能穿越中间的墙,以最少的步骤到达红色的终点

在解决这个问题之前,先要引入A*搜索算法的核心集合和公式:

核心集合:OpenList,CloseList

核心公式:F=G+H

其中,OpenList和CloseList用来存储格子点信息,OpenList表示可到达格子节点集合,CloseList表示已到达格子节点集合。

F=G+H表示对格子价值的评估,G表示从起点到当前点的代价;H表示从当前点到达终点的代价,指不考虑障碍物遮挡的情况下,这里,代价是指走的步数。至于F,就是对G和H的综合评估了,当然F越小,则从起点到达终点付出的代价就越小了。

就实际操作一下吧。还是上面的图,每个节点,用n(x,y)表示,x表示横坐标,y表示纵坐标,比如绿色的起点是n(1,2):

第一步:把起点放入OpenList里面。

OpenList: n(1,2)

CloseList

第二步:找出OpenList中F值最小的方格,即唯一的方格n(1,2)作为当前方格,并把当前格移出OpenList,放入CloseList。表示这个格子已到达且验证过了。

OpenList

CloseList:n(1,2)

第三步:找出当前格上下左右所有可到达的格子,看它们是否在OpenList当中。如果不在,加入OpenList,计算出相应的G、H、F值,并把当前格子作为它们的“父节点”。

OpenList:n(0,2), n(1,1), n(2,2), n(1,3)

CloseList:n(1,2)

其中,n(0,2), n(1,1), n(2,2), n(1,3)的父节点是n(1,2).所谓的父节点,表示当前的这几个节点n(0,2), n(1,1), n(2,2), n(1,3)都是从这个所谓的父节点出发得到的分支节点,父节点用作后续找出最短路径用的

上述3步,是一次局部寻路的过程,我们需要不断的重复第二步第三步,最终找到到达终点的最短路径。

第二轮 ~ 第一步:找出OpenList中F值最小的方格,即方格n(2,2)作为当前方格,并把当前格移出OpenList,放入CloseList。代表这个格子已到达并检查过了。

此时的两个核心集合的节点信息:

OpenList:n(0,2), n(1,1), n(1,3)

CloseList:n(1,2), n(2,2)

其中,n(0,2), n(1,1), n(1,3)的父节点是n(1,2),n(2,2)的上一级节点(也可以称为父节点)是n(1,2).

第二轮 ~ 第二步:找出当前格上下左右所有可到达的格子,看它们是否在OpenList当中。如果不在,加入OpenList,计算出相应的G、H、F值,并把当前格子作为它们的“父节点”。

此时的两个核心集合的节点信息:

OpenList:n(0,2), n(1,1), n(1,3);n(2,1), n(2,3)

CloseList:n(1,2) <-----n(2,2)

其中,n(0,2), n(1,1), n(1,3)的父节点是n(1,2),而n(2,1), n(2,3)的父节点是n(2,2). CloseList中节点的指向关系,反映了寻路的路径过程。

为什么这一次OpenList只增加了两个新格子呢?因为n(3,2)是墙壁,自然不用考虑,而n(1,2)在CloseList当中,说明已经检查过了,也不用考虑。

第三轮 ~ 第一步:找出OpenList中F值最小的方格。由于这时候多个方格的F值相等,任意选择一个即可,比如n(2,3)作为当前方格,并把当前格移出OpenList,放入CloseList。代表这个格子已到达并检查过了 

此时的两个核心集合的节点信息:

OpenList:n(0,2), n(1,1), n(1,3);n(2,1)

CloseList:n(1,2) <-----n(2,2)<-----n(2,3)

其中,n(0,2), n(1,1), n(1,3)的父节点是n(1,2),而n(2,1)的父节点是n(2,2)。CloseList中节点的指向关系,反映了寻路的路径过程。

第三轮 ~ 第二步:找出当前格上下左右所有可到达的格子,看它们是否在OpenList当中。如果不在,加入OpenList,计算出相应的G、H、F值,并把当前格子作为它们的“父节点”。

此时的两个核心集合的节点信息:

OpenList:n(0,2), n(1,1), n(1,3);n(2,1) ;n(2,4)

CloseList:n(1,2) <-----n(2,2)<-----n(2,3)

其中,n(0,2), n(1,1), n(1,3)的父节点是n(1,2),而n(2,1)的父节点是n(2,2)。n(2,4)的父节点是n(2,3). CloseList中节点的指向关系,反映了寻路的路径过程。

剩下的就是以前面的方式继续迭代,直到OpenList中出现终点方格为止。

实际的推理,就到这里,下面,将结合上述的推理理论,用java程序,加以实现。今天,先将伪代码附上,改天将具体的java实现代码贴上来。

public Node AStarSearch(Node start, Node end) {
// 把起点加入openList
openList.add(start);
//主循环,每一轮检查一个当前方格节点
while (openList.size() > ) {
// 在OpenList中查找F值最小的节点作为当前方格节点
Node current = findMinNode();
// 当前方格节点从open list中移除
openList.remove(current);
// 当前方格节点进入closeList
closeList.add(current);
// 找到所有邻近节点
List<Node> neighbors = findNeighbors(current);
for (Node node : neighbors) {
if (!openList.contains(node)) {
//邻近节点不在openList中,标记父亲、G、H、F,并放入openList
markAndInvolve(current, end, node);
}
}
//如果终点在OpenList中,直接返回终点格子
if (find(openList, end) != null) {
return find(openList, end);
}
}
//OpenList用尽,仍然找不到终点,说明终点不可到达,返回空
return null;
}

2017-10-13  11:28

过了几天了,今天终于回来补全未完成的最终实现代码逻辑,直接上代码:

package com.shihuc.nlp.astarsearch;

import java.io.File;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner; class Node {
int x; //当前节点的x坐标
int y; //当前节点的y坐标
int px; //指向父节点的x坐标
int py; //指向父节点的y坐标
} public class Solution {
static List<Node> openList = new ArrayList<Node>();
static List<Node> closeList = new ArrayList<Node>(); /**
* @param args
* @throws FileNotFoundException
*/
public static void main(String[] args) throws FileNotFoundException { File file = new File("./src/com/shihuc/nlp/astarsearch/sample.txt");
Scanner sc = new Scanner(file);
int N = sc.nextInt();
for (int n = ; n < N; n++) {
int Y = sc.nextInt();
int X = sc.nextInt();
int sx = sc.nextInt();
int sy = sc.nextInt();
int ex = sc.nextInt();
int ey = sc.nextInt();
Node start = new Node();
start.x = sx; start.y = sy;
Node end = new Node();
end.x = ex; end.y = ey;
int grid[][] = new int[X][Y];
openList.clear();
closeList.clear();
for (int x = ; x < X; x++) {
for (int y = ; y < Y; y++) {
grid[x][y] = sc.nextInt();
}
}
Node ne = AStarSearch(start,end, grid);
if(ne == null){
System.out.println("No." + n + " Can not reach the end node");
}else{
add2Cl(ne);
//printRawPath(n);
printRealPath(n, start);
}
}
if(sc != null){
sc.close();
}
} /**
* 打印当前节点以及其父节点的debug函数,查看父子节点关系
*
* @author shihuc
* @param idx
*/
public static void printRawPath(int idx){
System.out.println("No." + idx);
for(Node p: closeList){
System.out.println("([" + p.x + "," + p.y + "][" + p.px + "," + p.py + "])" );
}
System.out.println();
} /**
* 打印最终的路径信息的输出函数,起点节点用于输出结束判决
*
* @author shihuc
* @param start
*/
public static void printRealPath(int idx, Node start){
List<Node> path = new ArrayList<Node>();
Node cn = closeList.get(closeList.size() - );
Node temp = new Node();
temp.x = cn.x;temp.y = cn.y;temp.px = cn.px;temp.py = cn.py;
path.add(cn);
do{
for(int i=; i<closeList.size(); i++){
Node pn = closeList.get(i);
if(temp.px == pn.x && temp.py == pn.y){
temp.px = pn.px;
temp.py = pn.py;
temp.x = pn.x;
temp.y = pn.y;
path.add(pn);
closeList.remove(pn);
break;
}
}
}while(!(temp.x == start.x && temp.y == start.y));
System.out.print("No." + idx + " ");
for(int i=path.size()- ; i >=; i--){
Node n = path.get(i);
System.out.print("[" + n.x + "," + n.y + "]->");
}
System.out.println();
} /**
* A*搜索的完整算法实现。
*
* @author shihuc
* @param start 起点的坐标
* @param end 目标点的坐标
* @param grid 待搜索的网络
* @return
*/
public static Node AStarSearch(Node start, Node end, int grid[][]) {
// 把起点加入 openList
add2Ol(start);
// 主循环,每一轮检查一个当前方格节点
while (openList.size() > ) {
// 在OpenList中查找 F值最小的节点作为当前方格节点
Node current = findMinNode(start,end);
// 当前方格节点从openList中移除
remove4Ol(current);
// 当前方格节点进入 close list
add2Cl(current);
// 找到所有邻近节点
List<Node> neighbors = findNeighbors(current, grid);
for (Node node : neighbors) {
if (openListMarkedNode(node) == null) {
//邻近节点不在OpenList中,标记父节点,并放入OpenList
markAndInvolve(current, node);
}
}
// 如果终点在OpenList中,直接返回终点格子
Node last = findInOpenList(end);
if ( last != null) {
return last;
}
}
// OpenList用尽,仍然找不到终点,说明终点不可到达,返回空
return null;
} /**
* 向openList添加节点。若节点已经存在,则不添加。
*
* @author shihuc
* @param n 待添加的节点
*/
private static void add2Ol(Node n){
if(openListMarkedNode(n) == null){
openList.add(n);
}
} /**
* 向closeList添加节点信息。若节点已经存在,则不添加。
*
* @author shihuc
* @param n
*/
private static void add2Cl(Node n){
for(Node pn: closeList){
if(pn.x == n.x && pn.y == n.y){
return;
}
}
closeList.add(n);
} /**
* 从openList中删除指定的节点。通过坐标信息定位指定节点。
*
* @author shihuc
* @param n
*/
private static void remove4Ol(Node n){
for(Node ne:openList){
if(ne.x == n.x && ne.y == n.y){
openList.remove(ne);
return;
}
}
} /**
* openlist中若有已经标记的指定节点,则返回该节点,否则返回null节点。
*
* @author shihuc
* @param n
* @return
*/
private static Node openListMarkedNode(Node n){
for(Node ne: openList){
if(ne.x == n.x && ne.y == n.y){
return ne;
}
}
return null;
} /**
* 从closeList检查是否存在指定的节点。
*
* @author shihuc
* @param n
* @return
*/
private static boolean isInCloseList(Node n){
for(Node pn: closeList){
if(pn.x == n.x && pn.y == n.y){
return true;
}
}
return false;
} /**
* 利用类似勾股定理的方式计算H值以及G值。
*
* @author shihuc
* @param x
* @param y
* @return
*/
private static int gouguLaw(int x, int y){
return x*x + y*y;
} /**
* 在openList中查找F=G+H的值最小的节点。
*
* @author shihuc
* @param start
* @param end
* @return
*/
public static Node findMinNode(Node start, Node end){
int fMin = ;
int sx = start.x, sy = start.y;
int ex = end.x, ey = end.y;
Node nm = new Node();
Node n0 = openList.get();
nm.x = n0.x;nm.y = n0.y;
fMin = gouguLaw(n0.x - sx, n0.y - sy) + gouguLaw(n0.x - ex, n0.y - ey);
for(int i=; i<openList.size(); i++){
Node n = openList.get(i);
int g = gouguLaw(n.x - sx, n.y - sy);
int h = gouguLaw(n.x - ex, n.y - ey);
if(fMin > g+h){
nm.x = n.x;
nm.y = n.y;
nm.px = n.px;
nm.py = n.py;
fMin = g+h;
}
}
return nm;
} /**
* 以当前节点为中心,查找没有验证过(不在closeList中)的上下左右邻居节点。
*
* @author shihuc
* @param current
* @param grid
* @return
*/
public static List<Node> findNeighbors(Node current, int grid[][]){
int x = current.x;
int y = current.y; int Y = grid.length;
int X = grid[].length; List<Node> neigs = new ArrayList<Node>();
if(x - >= && grid[y][x - ] != ){
Node nu = new Node();
nu.x = x - ;
nu.y = y;
if(!isInCloseList(nu)){
neigs.add(nu);
}
}
if(x + < X && grid[y][x+] != ){
Node nu = new Node();
nu.x = x + ;
nu.y = y;
if(!isInCloseList(nu)){
neigs.add(nu);
}
}
if(y - >= && grid[y - ][x] != ){
Node nu = new Node();
nu.x = x;
nu.y = y - ;
if(!isInCloseList(nu)){
neigs.add(nu);
}
}
if(y + < Y && grid[y + ][x] != ){
Node nu = new Node();
nu.x = x;
nu.y = y + ;
if(!isInCloseList(nu)){
neigs.add(nu);
}
}
return neigs;
} /**
* 检查指定节点是否在openList中。
*
* @author shihuc
* @param ed
* @return
*/
public static Node findInOpenList(Node ed){
return openListMarkedNode(ed);
} /**
* 这个函数非常重要,标记当前节点的邻居节点的父亲节点为当前节点,这个标记关系,用于后续输出A*搜索的最终路径
*
* @author shihuc
* @param current
* @param n
*/
public static void markAndInvolve(Node current, Node n){
n.px = current.x;
n.py = current.y;
openList.add(n);
}
}

测试用到的数据样本(sample.txt内容):


最终的数据测试结果:

No. [,]->[,]->[,]->[,]->[,]->[,]->[,]->[,]->[,]->
No. Can not reach the end node
No. [,]->[,]->[,]->[,]->[,]->[,]->[,]->[,]->[,]->[,]->[,]->[,]->[,]->[,]->[,]->[,]->[,]->[,]->[,]->[,]->[,]->[,]->[,]->[,]->[,]->[,]->[,]->[,]->[,]->[,]->[,]->[,]->

上述算法,场景比较简单,当前节点的上下左右四个方向,有的要求8个方向的,甚至障碍物有其他的要求的。都离不开这里的最要思想,F=G+H.

欢迎探讨,欢迎评论以及转帖,请注明出处!

A* search算法的更多相关文章

  1. 从vector容器中查找一个子串:search()算法

    如果要从vector容器中查找是否存在一个子串序列,就像从一个字符串中查找子串那样,次数find()与find_if()算法就不起作用了,需要采用search()算法:例子: #include &qu ...

  2. Breadth-first search 算法(Swift版)

    在讲解Breadth-first search 算法之前,我们先简单介绍两种数据类型Graph和Queue. Graph 这就是一个图,它由两部分组成: 节点, 使用圆圈表示的部分 边, 使用线表示的 ...

  3. 对《禁忌搜索(Tabu Search)算法及python实现》的修改

    这个算法是在听北大人工智能mooc的时候,老师讲的一种局部搜索算法,可是举得例子不太明白.搜索网页后,发现<禁忌搜索(Tabu Search)算法及python实现>(https://bl ...

  4. A* search算法解迷宫

    这是一个使用A* search算法解迷宫的问题,细节请看:http://www.laurentluce.com/posts/solving-mazes-using-python-simple-recu ...

  5. 【优化算法】Greedy Randomized Adaptive Search算法 超详细解析,附代码实现TSP问题求解

    01 概述 Greedy Randomized Adaptive Search,贪婪随机自适应搜索(GRAS),是组合优化问题中的多起点元启发式算法,在算法的每次迭代中,主要由两个阶段组成:构造(co ...

  6. 关于 Image Caption 中测试时用到的 beam search算法

    关于beam search 之前组会中没讲清楚的 beam search,这里给一个案例来说明这种搜索算法. 在 Image Caption的测试阶段,为了得到输出的语句,一般会选用两种搜索方式,一种 ...

  7. R-CNN目标检测的selective search(SS算法)

    候选框确定算法 对于候选框的位置确定问题,简单粗暴的方法就是穷举或者说滑动窗口法,但是这必然是不科学的,因为时间和计算成本太高,直观的优化就是假设同一种物体其在图像邻域内有比较近似的特征(例如颜色.纹 ...

  8. 算法-search

    O(big o) 是上限,是我们关注的算法的时间复杂度.数据量大,数据量涨一千倍,lgn的算法就是 耗费的时间就是10倍,o(n)就是一千倍,o(n2)就是一百万倍的差距 例一:Sequential ...

  9. 常用查找数据结构及算法(Python实现)

    目录 一.基本概念 二.无序表查找 三.有序表查找 3.1 二分查找(Binary Search) 3.2 插值查找 3.3 斐波那契查找 四.线性索引查找 4.1 稠密索引 4.2 分块索引 4.3 ...

随机推荐

  1. 简短而有效的python queue队列解释

    Queue.qsize() 返回队列的大小  Queue.empty() 如果队列为空,返回True,反之False  Queue.full() 如果队列满了,返回True,反之False Queue ...

  2. python的time模块

    #encoding=utf-8 import time # 返回时间戳 print time.time() # 延迟运行单位为s,如下为延迟3s time.sleep(3) # 转换时间戳为时间元组( ...

  3. 在使用MyCat和MySqL时的错误总结

    在mysql中,无法连接虚拟机中的mysql. 原因:防火墙没有关闭 解决方案:service iptables stop 在mycat中,无法打开数据库的表, 原因:mycat在配置文件中设置的是自 ...

  4. eact native生成APP报错:You have not accepted the license agreements of the following SDK components:

    一.报错信息 * What went wrong: A problem occurred configuring project ':app'. > You have not accepted ...

  5. 给Ubuntu软件升级命令

    以非root用户更新系统 sudo: sudo是linux系统管理指令,是允许系统管理员让普通用户执行一些或者全部的root命令的一个工具,如halt,reboot,su等等.这样不仅减少了root用 ...

  6. Pytorch中的Batch Normalization操作

    之前一直和小伙伴探讨batch normalization层的实现机理,作用在这里不谈,知乎上有一篇paper在讲这个,链接 这里只探究其具体运算过程,我们假设在网络中间经过某些卷积操作之后的输出的f ...

  7. C++ 中时钟函数的使用

    头文件: #incllude <time.h> 定义: clock_t var1, var2; 获取时间: var1 = clock(); 每过千分之一秒(1毫秒),调用clock()函数 ...

  8. WCF 添加服务引用 HTTP 请求已超过为 00:00:00 分配的超时。为此操作分配的时间可能是较长超时

    今天在用公司的笔记本引用WCF的时候,处于一直等待的过程,一直在下载信息,一直等了很长时间,弹出了一个消息 下载“http://ip:8085/xxxxx/xxxxx/mex/$metadata”时出 ...

  9. SQLAlchemy中表结构的一对多

    from flask import Flask from flask_sqlalchemy import SQLAlchemy import pymysql pymysql.install_as_My ...

  10. [LeetCode&Python] Problem 383. Ransom Note

    Given an arbitrary ransom note string and another string containing letters from all the magazines, ...