A*搜索是最佳优先搜索最广为人知的形式,是一种有信息搜索策略,它的核心是一个估值函数:f(n)=g(n)+h(n),g(n)是从起始点到节点n的路径耗散,而h(n)是从节点n到目标节点的最低耗散路径的估计耗散值,因此f(n)=经过节点n的最低耗散解的估计耗散。
完备性证明:A*搜索能够找到最低耗散解的依据是一个可采纳启发式:h(n)不会高估经过节点n的实际耗散,采用可采纳启发式h(n)如果有个非最优目标节点G率先被搜索到,此时f(G) = g(G) + h(G) = g(G) > 最低耗散C*,而此时一定存在最低耗散路径上的节点n,f(n) = g(n) + h(n) <= C*,因此G不会被采纳,搜索始终会终止于一个最优解。如果一个启发式满足一致性,也就是:如果对于每个节点n和通过任何行动a生成的n的每个后继节点n',从节点n到达目标的估计号耗散值不大于从n到n'的但不耗散与从n'到达目标的耗散值之和,即h(n) <= c(n,a,n') + h(n'),类似于三角形不等式,很容易证明满足一致性的启发式就是可采纳启发式。
伪代码:
<span style="font-family:Microsoft YaHei;font-size:14px;"><span style="background-color: rgb(204, 204, 204);"><span style="font-size:12px;">open ← {s}; closed ← Ø while open != Ø do remove leftmost state from open, call it x if x is a goal then return success else generate children of x for each child c do if c is already on open list then if c was reached by a shorter path then update f(c) else if c is already on closed list then if c was reached by a shorter path then remove c from closed list add c to open list and update f(c) else assign c a heuristic value f(c) add c to open list put x on closed list and reorder states on open by f return failure</span></span> </span>
曾经写过的HDU1043八数码问题用了A*搜索:
<span style="font-family:Microsoft YaHei;font-size:14px;"> <span style="font-size:12px;">#include<iostream> #include<queue> #include<stack> #include<memory.h> #include<math.h> #include<algorithm> #define INF 400000 #define END 322560 using namespace std; struct NODE { int board[3][3]; int x,y; int h,g; int state; bool operator < (const NODE & node) const { return (h + g) > (node.h + node.g); } }sta; int dir1[4][2] = {{0,-1},{0,1},{-1,0},{1,0}}; char dir2[4] = {'l','r','u','d'}; int fact[9] = {1,1,2,6,24,120,720,5040,40320}; int vis[INF],pre[INF]; void init(char *); int get_hash(const NODE &); void aStar(); void print(); int check(const NODE &); int val(const NODE &); int main() { char str[100]; // freopen("Sample Input.txt","r",stdin); while(cin.getline(str,100)) { init(str); if(!check(sta)) { cout << "unsolvable" << endl; } else { aStar(); print(); } } return 0; } void init(char str[]) { int pos = 0; for(int i = 0;i < 3;i++) { for(int j = 0;j < 3;j++) { int pos = (3 * i + j) * 2; if(str[pos] == 'x') { sta.board[i][j] = 0; sta.x = i; sta.y = j; } else { sta.board[i][j] = str[pos] - '0'; } } } sta.g = 0; sta.h = val(sta); sta.state = get_hash(sta); memset(vis,-1,sizeof(vis)); memset(pre,-1,sizeof(pre)); vis[sta.state] = 1; } int get_hash(const NODE & node) //用康托展开获得此时排列在所有排列中的位置 { int num[9]; for(int i = 0;i < 3;i++) { for(int j = 0;j < 3;j++) { num[3 * i + j] = node.board[i][j]; } } int res = 0; for(int i = 0;i < 9;i++) { int cnt = 0; for(int j = 0;j < i;j++) { if(num[j] > num[i]) { cnt++; } } res += cnt * fact[i]; } return res; } void aStar() { priority_queue<NODE> que; que.push(sta); while(!que.empty()) { NODE cur = que.top(); que.pop(); if(cur.state == END) { return; } for(int i = 0;i < 4;i++) { NODE next = cur; next.x += dir1[i][0]; next.y += dir1[i][1]; if(next.x < 0 || next.y < 0 || next.x > 2 || next.y > 2) { continue; } swap(next.board[cur.x][cur.y],next.board[next.x][next.y]); next.state = get_hash(next); if(vis[next.state] < 0) { vis[next.state] = i; pre[next.state] = cur.state; next.g++; next.h = val(next); que.push(next); } } } } void print() { int pos = END; stack<char> path; while(pre[pos] != -1) { path.push(dir2[vis[pos]]); pos = pre[pos]; } while(!path.empty()) { cout << path.top(); path.pop(); } cout << endl; } int check(const NODE & node) //剪枝 { int num[9]; for(int i = 0;i < 3;i++) { for(int j = 0;j < 3;j++) { num[3 * i + j] = node.board[i][j]; } } int cnt = 0; for(int i = 0;i < 9;i++) { if(num[i] == 0) { continue; } for(int j = 0;j < i;j++) { if(num[j] != 0 && num[j] > num[i]) { cnt++; } } } if(cnt % 2 == 0) return 1; else return 0; } int val(const NODE & node) //没有X时的曼哈顿距离,很明显符合一致性 { int sum = 0; for(int i = 0;i < 3;i++) { for(int j = 0;j < 3;j++) { if(node.board[i][j]) { sum += abs((node.board[i][j] - 1) / 3 - i) + abs((node.board[i][j] - 1) % 3 - j); } } } return sum; }</span> </span>