2015-1-24 更新,发现错误,AStar.cpp文件的60行,原本的'>'改为'<'。。。。。
A星寻路算法真是我一生接触的第一个人工智能算法了。。。
A星寻路算法显然是用来寻路的,应用也很普遍,比如梦幻西游。。。算法的思路很简单,就是在bfs的基础上加了估值函数。
它的核心是 F(x) = G(x) + H(x) 和open、close列表:
G(x)表示从起点到X点的消耗(或者叫移动量什么的),H(X)表示X点到终点的消耗的估值,F(x)就是两者的和值。open列表记录了可能要走的区域,close列表记录了不会再考虑的区域。我们每次都选F值最小的区域搜索,就能搜到一条到终点的最短路径,其中估值H越接近准确值,需要搜索的节点就越少。
A星算法的步骤:
{
将起点区域添加到open列表中,该区域有最小的和值。
重复以下:
将open列表中最小F值的区域X移除,然后添加到close列表中。
对于与X相邻的每一块可通行且不在close列表中的区域T:
如果T不在open列表中:添加到open列表,把X设为T的前驱
如果T已经在open列表中:检查 F 是否更小。如果是,更新 F 和前驱
直到:
终点添加到了close列表。(已找到路径)
终点未添加到close列表且open列表已空。(未找到路径)
}
Golang实现如下:
(应该把OpenList和CloseList做成接口更好,赶时间先这样写了。。。)
1 package astar 2 3 import ( 4 "container/heap" 5 "container/list" 6 "math" 7 ) 8 9 type Point struct { 10 X int 11 Y int 12 } 13 14 func (a Point) getManhattanDistance(b Point) int { 15 return int(math.Abs(float64(a.X-b.X)) + math.Abs(float64(a.Y-b.Y))) 16 } 17 18 type Area struct { 19 Point 20 G int 21 H int 22 } 23 24 type CloseList [][]bool 25 26 func (CloseList) New(x, y int) CloseList { 27 res := make([][]bool, x) 28 for i := 0; i < x; i++ { 29 res[i] = make([]bool, y) 30 } 31 return res 32 } 33 34 type OpenList []Area 35 36 func (ls OpenList) Len() int { 37 return len(ls) 38 } 39 40 func (ls OpenList) Less(a, b int) bool { 41 return ls[a].G < ls[b].G 42 } 43 44 func (ls OpenList) Swap(a, b int) { 45 ls[a], ls[b] = ls[b], ls[a] 46 } 47 48 func (ls *OpenList) Push(x interface{}) { 49 *ls = append(*ls, x.(Area)) 50 } 51 52 func (ls *OpenList) Pop() interface{} { 53 lenth := (*ls).Len() 54 res := (*ls)[lenth-1] 55 *ls = (*ls)[0 : lenth-1] 56 return res 57 } 58 59 func getPath(start, end Point, pathPre map[Point]Point) *list.List { 60 pathList := list.New() 61 pathCur := end 62 for !(pathCur == start) { 63 pathList.PushFront(pathCur) 64 pathCur = pathPre[pathCur] 65 } 66 pathList.PushFront(start) 67 return pathList 68 } 69 70 func Search(legal [][]int, start, end Point) (int, *list.List) { 71 row := len(legal) 72 column := len(legal[0]) 73 74 var closeList CloseList 75 closeList = closeList.New(row, column) 76 77 var openList OpenList 78 openList = make([]Area, row*column) 79 80 pathPre := map[Point]Point{} 81 82 dir := [][]int{{0, 1}, {0, -1}, {1, 0}, {-1, 0}} 83 84 openList[0] = Area{start, 0, 0} 85 86 for len(openList) > 0 { 87 cur := openList.Pop().(Area) 88 closeList[cur.X][cur.Y] = true 89 if cur.Point == end { 90 return cur.G, getPath(start, end, pathPre) 91 } 92 for _, v := range dir { 93 x := cur.X + v[0] 94 y := cur.Y + v[1] 95 g := cur.G + 1 96 h := end.getManhattanDistance(cur.Point) 97 if x >= 0 && x < row && y >= 0 && y < column && legal[x][y] != 0 && !closeList[x][y] { 98 inopen := false 99 tar := Area{Point{x, y}, g, h} 100 for index := 0; index < openList.Len(); index++ { 101 if openList[index].Point == tar.Point { 102 inopen = true 103 if openList[index].G > g { 104 openList[index].G = g 105 pathPre[tar.Point] = cur.Point 106 break 107 } 108 } 109 } 110 if !inopen { 111 heap.Push(&openList, tar) 112 pathPre[tar.Point] = cur.Point 113 } 114 } 115 } 116 } 117 return -1, nil 118 }
1 package main 2 3 import ( 4 "ex1/astar" 5 "fmt" 6 ) 7 8 func main() { 9 legal := [][]int{ 10 {0, 0, 0, 1, 1, 1, 1}, 11 {0, 0, 1, 1, 0, 1, 0}, 12 {1, 1, 1, 1, 1, 0, 0}, 13 {1, 0, 0, 1, 0, 0, 0}, 14 {1, 1, 1, 1, 1, 1, 1}, 15 } 16 start := astar.Point{0, 6} 17 end := astar.Point{4, 6} 18 lenth, path := astar.Search(legal, start, end) 19 fmt.Println(lenth) 20 21 for p := path.Front(); p != nil; p = p.Next() { 22 fmt.Println(p.Value) 23 } 24 }
C++实现如下:
1 /*************************** 2 * A星寻路算法 3 * 4 * Anti-Magic 5 * 2014/5/26 6 ****************************/ 7 8 #ifndef __ASTAR_H__ 9 #define __ASTAR_H__ 10 11 #include <map> 12 #include <set> 13 #include <vector> 14 #include <list> 15 16 //属性宏 17 #define SYNTHESIZE(varType, varName, funName) \ 18 protected: \ 19 varType varName; \ 20 public: \ 21 virtual varType get##funName(void) const { \ 22 return varName; \ 23 } \ 24 virtual void set##funName(varType var) { \ 25 varName = var; \ 26 } 27 28 /*************************** 29 * Point类 30 * 表示一个二维坐标 31 ****************************/ 32 class Point 33 { 34 SYNTHESIZE(int, _x, X); 35 SYNTHESIZE(int, _y, Y); 36 public: 37 Point(); 38 Point(int x, int y); 39 //获得曼哈顿距离 40 int getManhattanDistance(const Point& t) const; 41 //获得欧几里得距离 42 double getEuclidDistance(const Point& t) const; 43 bool operator== (const Point& t) const; 44 bool operator< (const Point& t) const; 45 }; 46 47 /*************************** 48 * Local类 49 * 在A星寻路算法中的单位区域 50 ****************************/ 51 class Local : public Point 52 { 53 SYNTHESIZE(int, _g, G); 54 SYNTHESIZE(int, _h, H); 55 public: 56 Local(); 57 Local(int x, int y, int g, int h); 58 Local(const Point& t, int g, int h); 59 Local(const Local& t); 60 int getF() const; 61 bool operator< (const Local& t) const; 62 }; 63 64 /*************************** 65 * SuperCloseList类 抽象类 66 * Close列表的基类 67 * 因为实现Close列表在不同场合有不同的实现 68 * 需要继承此类 69 ****************************/ 70 class SuperCloseList 71 { 72 public: 73 //插入close列表 74 virtual void insert(const Local& p) = 0; 75 //查询close列表中是否已存在此区域 76 virtual bool find(const Local& p) const = 0; 77 }; 78 79 /*************************** 80 * CloseList类 81 * Close列表的哈希实现 82 ****************************/ 83 class CloseList : public SuperCloseList 84 { 85 public: 86 CloseList(); 87 CloseList(int x, int y); 88 ~CloseList(); 89 void insert(const Local& p) override; 90 bool find(const Local& p) const override; 91 private: 92 bool** _close; 93 int size_x, size_y; 94 //申请数组内存 95 void newClose(int x, int y); 96 }; 97 98 /*************************** 99 * A星算法类 100 * 实现A星算法的逻辑 101 ****************************/ 102 class AStar 103 { 104 public: 105 AStar(std::vector<std::vector<bool> >& legal, Point& start, Point& end); 106 //获得距离 107 int getDistance() const; 108 //获得路径 109 std::list<Point> getPath() const; 110 private: 111 std::vector<std::vector<bool> > _legal; 112 std::multiset<Local> open; 113 CloseList close; 114 Point _start; 115 Point _end; 116 std::map<Point, Point> _path_pre; 117 std::list<Point> _path_list; 118 int _distance; 119 //路径规划,算法的核心 120 void pathPlanning(); 121 //检查区域 122 void checkLocal(Local& local_tar, Local& local_cur); 123 //生成路径 124 void initPath(); 125 }; 126 127 #endif
1 #include "AStar.h" 2 3 Point::Point() 4 { 5 _x = _y = 0; 6 } 7 Point::Point(int x, int y) 8 { 9 _x = x; 10 _y = y; 11 } 12 int Point::getManhattanDistance(const Point& t) const 13 { 14 return std::abs(_x - t._x) + std::abs(_y - t._y); 15 } 16 double Point::getEuclidDistance(const Point& t) const 17 { 18 return std::sqrt((_x - t._x) * (_x - t._x) + (_y - t._y) * (_y - t._y)); 19 } 20 bool Point::operator== (const Point& t) const 21 { 22 return _x == t._x && _y == t._y; 23 } 24 bool Point::operator< (const Point& t) const 25 { 26 if (_x == t._x) 27 { 28 return _y < t._y; 29 } 30 return _x < t._x; 31 } 32 33 Local::Local() : Point() 34 { 35 _g = _h = 0; 36 } 37 Local::Local(int x, int y, int g, int h) : Point(x, y) 38 { 39 _g = g; 40 _h = h; 41 } 42 Local::Local(const Point& t, int g, int h) : Point(t) 43 { 44 _g = g; 45 _h = h; 46 } 47 Local::Local(const Local& t) 48 { 49 _x = t._x; 50 _y = t._y; 51 _g = t._g; 52 _h = t._h; 53 } 54 int Local::getF() const 55 { 56 return _g + _h; 57 } 58 bool Local::operator< (const Local& t) const 59 { 60 return getF() < t.getF(); 61 } 62 63 CloseList::CloseList() 64 { 65 newClose(100, 100); 66 } 67 CloseList::CloseList(int x, int y) 68 { 69 newClose(x, y); 70 } 71 CloseList::~CloseList() 72 { 73 for (int i = 0; i < size_y; i++) 74 { 75 delete[] _close[i]; 76 } 77 delete[] _close; 78 } 79 void CloseList::insert(const Local& p) 80 { 81 _close[p.getX()][p.getY()] = true; 82 } 83 bool CloseList::find(const Local& p) const 84 { 85 return _close[p.getX()][p.getY()] == true; 86 } 87 void CloseList::newClose(int x, int y) 88 { 89 size_x = x; 90 size_y = y; 91 _close = new bool*[x]; 92 for (int i = 0; i < x; i++) 93 { 94 _close[i] = new bool[y]; 95 memset(_close[i], false, sizeof(_close[i])); 96 } 97 } 98 99 AStar::AStar(std::vector<std::vector<bool> >& legal, Point& start, Point& end) 100 { 101 _legal = legal; 102 _start = start; 103 _end = end; 104 pathPlanning(); 105 } 106 int AStar::getDistance() const 107 { 108 return _distance; 109 } 110 std::list<Point> AStar::getPath() const 111 { 112 return _path_list; 113 } 114 void AStar::pathPlanning() 115 { 116 open.insert(Local(_start, 0, 0)); 117 int dir[][2] = { 118 { 0, 1 }, { 0, -1 }, 119 { 1, 0 }, { -1, 0 } 120 }; 121 while (!open.empty()) 122 { 123 Local cur = *open.begin(); 124 open.erase(open.begin()); 125 close.insert(cur); 126 if (cur == _end) 127 { 128 initPath(); 129 _distance = cur.getG(); 130 return; 131 } 132 for (int i = 0; i < 4; i++) 133 { 134 int x = cur.getX() + dir[i][0]; 135 int y = cur.getY() + dir[i][1]; 136 int g = cur.getG() + 1; 137 int h = _end.getManhattanDistance(Point(x, y)); 138 Local point_choose(x, y, g, h); 139 checkLocal(point_choose, cur); 140 } 141 } 142 _distance = -1; 143 } 144 void AStar::checkLocal(Local& local_tar, Local& local_cur) 145 { 146 int x = local_tar.getX(); 147 int y = local_tar.getY(); 148 int g = local_tar.getG(); 149 int h = local_tar.getH(); 150 if (x < _legal.size() && x >= 0 && y < _legal[0].size() && y >= 0 && 151 _legal[x][y] && !close.find(local_tar)) 152 { 153 bool inopen = false; 154 for (std::multiset<Local>::iterator it = open.begin(); it != open.end(); it++) 155 { 156 if (it->getX() == x && it->getY() == y) 157 { 158 inopen = true; 159 if (it->getG() > g) 160 { 161 open.erase(it); 162 open.insert(local_tar); 163 _path_pre[local_tar] = local_cur; 164 break; 165 } 166 } 167 } 168 if (!inopen) 169 { 170 open.insert(local_tar); 171 _path_pre[local_tar] = local_cur; 172 } 173 } 174 } 175 void AStar::initPath() 176 { 177 Point path_res = _end; 178 while (!(path_res == _start)) 179 { 180 _path_list.push_front(path_res); 181 path_res = _path_pre[path_res]; 182 } 183 _path_list.push_front(_start); 184 }
测试代码:
1 #define _CRT_SECURE_NO_DEPRECATE 2 3 #include "AStar.h" 4 #include <stdio.h> 5 6 int main() 7 { 8 std::vector<std::vector<bool> > legal { 9 std::vector<bool> {0, 0, 0, 1, 1, 1, 1}, 10 std::vector<bool> {1, 1, 1, 1, 0, 1, 0}, 11 std::vector<bool> {1, 1, 1, 1, 1, 0, 0}, 12 std::vector<bool> {1, 0, 0, 0, 0, 0, 0}, 13 std::vector<bool> {1, 1, 1, 1, 1, 1, 1}, 14 }; 15 Point start(1, 5); 16 Point end(4, 5); 17 AStar* astar = new AStar(legal, start, end); 18 printf("%d\n", astar->getDistance()); 19 std::list<Point> path = astar->getPath(); 20 for (auto path_cur : path) 21 { 22 printf(".....%d %d\n", path_cur.getX(), path_cur.getY()); 23 } 24 delete astar; 25 system("pause"); 26 return 0; 27 }