关键词:
今天,还是国庆和中秋双节的时间节点,一个天气不错的日子,孩子已经早早的睡觉了,玩了一整天,也不睡觉,累的实在扛不住了,勉强洗澡结束,倒床即睡着的节奏。。。
不多说题外话,进入正题。
什么是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() > 0) { // 在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 = 0; 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 = 0; x < X; x++) { for (int y = 0; 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() - 1); 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=0; 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()-1 ; i >=0; 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() > 0) { // 在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 = 0; int sx = start.x, sy = start.y; int ex = end.x, ey = end.y; Node nm = new Node(); Node n0 = openList.get(0); 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=1; 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[0].length; List<Node> neigs = new ArrayList<Node>(); if(x - 1 >= 0 && grid[y][x - 1] != 1){ Node nu = new Node(); nu.x = x - 1; nu.y = y; if(!isInCloseList(nu)){ neigs.add(nu); } } if(x + 1 < X && grid[y][x+1] != 1){ Node nu = new Node(); nu.x = x + 1; nu.y = y; if(!isInCloseList(nu)){ neigs.add(nu); } } if(y - 1 >= 0 && grid[y - 1][x] != 1){ Node nu = new Node(); nu.x = x; nu.y = y - 1; if(!isInCloseList(nu)){ neigs.add(nu); } } if(y + 1 < Y && grid[y + 1][x] != 1){ Node nu = new Node(); nu.x = x; nu.y = y + 1; 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内容):
3 7 5 1 2 5 2 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 7 5 1 2 5 2 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 20 15 1 1 19 14 0 0 0 0 0 1 0 0 0 1 0 0 0 0 0 0 0 0 1 1 0 0 1 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 0 0 0 0 0 1 1 0 0 0 0 1 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 1 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 0 1 0 0 0 0 1 0 0 0 0 0 0 1 0 1 0 0 0 1 0 0 0 0 1 0 0 0 0 0 0 0 0 0 1 0 0 0 0 0
最终的数据测试结果:
No.0 [1,2]->[2,2]->[2,1]->[2,0]->[3,0]->[4,0]->[4,1]->[4,2]->[5,2]-> No.1 Can not reach the end node No.2 [1,1]->[1,2]->[2,2]->[3,2]->[4,2]->[4,3]->[4,4]->[5,4]->[6,4]->[7,4]->[7,5]->[7,6]->[8,6]->[9,6]->[9,7]->[10,7]->[11,7]->[11,8]->[11,9]->[12,9]->[std::search 中使用啥算法?】std::search中使用啥算法?【英文标题】:Whatalgorithmisusedinstd::search?std::search中使用什么算法?【发布时间】:2012-02-2715:42:15【问题描述】:有许多字符串匹配算法可用于在大文本中查找模式(字符串),如Boyer-Moore、Aho-Corasick等... 查看详情
a*算法解决八数码问题
fromutilsimport(PriorityQueue)importcopyinfinity=float(‘inf‘)defbest_first_graph_search(problem,f):#定义初始节点node=Node(problem.initial)node.fvalue=f(node)#如果是最终结果,返回节点ifproblem.goal_test(node):returnnode 查看详情
什么是 search.twitter.com 的“热门话题”算法?
】什么是search.twitter.com的“热门话题”算法?【英文标题】:Whatissearch.twitter.com\'s"trendingtopics"algorithm?【发布时间】:2010-09-1317:08:11【问题描述】:twitter使用什么算法来确定您可以在search.twitter.com看到的10个主题?我想实... 查看详情
用递归法写一个折半查找的算法
#include<stdio.h>intbin_search(intle,intri,intnum,intarr[])intmid;mid=((le+ri)>>1);if(le>ri)return-1;elseif(arr[mid]==num)returnmid;elseif(arr[mid]<num)returnbin_search(mid+1,ri,num,arr);elsereturnbin_search(le,mid-1,num,arr);intmain()intarr[101];intlocal=-1;for(inti=1;i<=100;i+... 查看详情
算法-单词的添加和查找
...意:设计一个包含下面两个操作的数据结构:addWord(word),search(word)addWord(word)会在数据结构中添加一个单词。而search(word)则支持普通的单词查询或是只包含.和a-z的简易正则表达式的查询。一个.可以代表一个任何的字母。样例:addW... 查看详情
c++之binary_search二分查找算法(代码片段)
C++之binary_search二分查找算法功能:查找指定元素是否存在,查到返回true否则false。函数原型:boolbinary_search(iteratorbeg,iteratorend,value);解释:beg开始迭代器end结束迭代器value查找的元素。注意🗣🗣: 查看详情
二分查找算法
二分查找算法就是不断将数组进行对半分割,每次拿中间元素和goal进行比较。#include <iostream>using namespace std;//二分查找int binary_search(int* a, int len, int goal);int main(){ 查看详情
算法|a*算法和权重a*算法(代码片段)
A*andWeightedA*Search思路启发式搜索算法要理解A*搜寻算法,还得从启发式搜索算法开始谈起。所谓启发式搜索,就在于当前搜索结点往下选择下一步结点时,可以通过一个启发函数(HeuristicFunction)来进行选择,选择代价最少的结点作... 查看详情
启发式搜索(heuristicsearch)———a*算法(代码片段)
...搜索效率远远大于盲搜。什么是启发式搜索(heuristic search) 利用当前与问题 查看详情
Binary Search RecursionError:比较超过最大递归深度
】BinarySearchRecursionError:比较超过最大递归深度【英文标题】:BinarySearchRecursionError:maximumrecursiondepthexceededincomparison【发布时间】:2022-01-2217:03:09【问题描述】:我试图在python中执行二进制搜索程序。我遵循了算法步骤,但它给... 查看详情
python算法:二分查找
...查找速度快,平均性能好,占用系统内存较少。defbinary_search(list,item):low=0high=len(list)-1whilelow<=high:mid=int((low+high)/2)#整除计算也可用mid=(low+high)//2guess=list[mid]ifguess==item:returnmidifguess<item:low=mid+1else:high=mid-1returnNonemy_list=[1,3,5,6,8,7,9... 查看详情
算法-基础和查找-1.汉诺塔/2.顺序查找/3.二分查找/4.顺序查找和二分查找的比较(代码片段)
...返回索引值,找不到返回None或-1 代码如下:1deflinear_search(li,val):2forindex,vinenumerate(li):3ifv==val:4returnindex5else:6returnNone78print(linear_search([2,3,4],4))linear_search 3.二分查找: 问题分析:在一个列表中查找一个元素,每次... 查看详情
新手算法学习之路----二分法search-a-2d-matrix
题目: 写出一个高效的算法来搜索 m × n矩阵中的值。 这个矩阵具有以下特性: &nb 查看详情
静态查找算法(代码片段)
...可以分为for循环查找和while循环查找1.For的循环查找intfor_Search(int*a,intn,intkey) inti; for(i=1;i<=n;i++) if(a[i] 查看详情
stl源代码剖析算法stl_algo.h--search
...senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenliesearch-------------------------------------------------------------------------描写叙述:在序列一[first1,last1)所涵盖的区间中。查找序列二[first2,last2)的首次出现点。思路:1.遍历序列二2 查看详情
acm算法回顾搜索
今天主要回顾一下几个搜索DFS ——DepthFirstSearchBFS ——BreadthFirstSearchA* 迭代优先搜索今天DFS和BFS的实现就不细讲了我们先直接看A*算法的实现(python风格为主...带一点伪代码)1defA_star_search2q=PriorityQueue()3#优先队列,顺... 查看详情
doa估计算法
...257D&request_id=160689878119725222413438&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~baidu_landing_v2~default-1-100730081.pc_first_rank_v2_rank_v28&utm_term=Musicsuanfa&spm=1018.2118.3001.4449DOA估计算法DOA(DirectionOfArrival)波达方向定位技术主要有ARMA谱... 查看详情
在 python 列表中使用正则表达式(re.search)
】在python列表中使用正则表达式(re.search)【英文标题】:Usingregex(re.search)inapythonlist【发布时间】:2015-08-0212:43:10【问题描述】:我有以下代码importrepattern=[\'A-minorTypeIAGC\',\'A-minorTypeIAGC\',\'A-minorTypeIAGC\',\'A-minorTypeIAUA\',\'A-minorType... 查看详情