利用图搜索来优化八数码问题的A*算法
2016-03-29 19:24
369 查看
在最开始的用A*算法的时候,我们使用树形结构来生成后继的拓展节点,导致我们的解空间是按指数增长,但是实际上我们的路径节点的总个数是一定的,相互链接成了一个图的结构,我们利用树形结构来生成子节点的时候实际上生成了重复的节点。所以图搜索的关键是对生成的重复节点的处理,从而减小了解空间。
灰色的区域是利用图搜索技术对算法进行改进。
下面是算法的为代码:
灰色的区域是利用图搜索技术对算法进行改进。
下面是C++代码:
#include <iostream> #include <fstream> #include <vector> #include <cmath> #include <list> #include <map> //#include "process.cpp"; #define SIZE 3//棋盘的大小 size*size using namespace std; /** *定义方案节点 */ typedef struct Node { vector<int> board; int rc; int h; int g; int parent;//在close中指示父亲节点的下表 }pNode; int x_axis[] = {-1, 0, 0, 1}; int y_axis[] = { 0, -1, 1, 0}; /** *输出方案 */ void print(vector<int> board, int rc) { cout<<"当前方案为:"<<endl; for(int i = 0; i < rc; ++i) { for(int j = 0; j < rc; ++j) { cout<<board[i*rc+j]<<' '; } cout<<endl; } //cout<<"当前方案的F值为:"<<endl; //cout<<board[board.size()-1]<<endl; return ; } /** *将遍历到的当前节点输出到文本文件中 */ void out_data(pNode &node) { int s = node.rc; ofstream outdata("result.txt",ios::app); outdata<<"第"<<node.parent<<"层数据"<<endl; for(int i = 0; i < s; i++) { for(int j = 0; j < s; j++) { outdata<<node.board[i*s+j]<<" "; } outdata<<endl; } outdata<<"h值:"<<node.h<<endl; outdata.flush(); outdata.close(); return ; } /* *判断棋盘是否有序 * *@param vector<int> board *@return bool是否有序 */ bool is_ordered(vector<int> board) { for(int i = 0; i < board.size(); ++i) { if(board[i] != i) { return false; } } return true; } /** *定义heuristic函数 探索函数 * *@param vector<int> board , int rc 表示解决方案是rc*rc的 r&c *@return int value of heuristic */ int heuristics(vector<int> board,int rc) { //表示元素的正确位置 int gx = 0; int gy = 0; //表示Manhattan block distance int distance = 0; for(int i = 0; i < rc*rc; ++i) { int nx = i / rc; int ny = i % rc; gx = board[i] / rc; gy = board[i] % rc; distance += abs(nx - gx) + abs(ny-gy); } return distance; } /** *same_plan()判断两个vector<int> 是否相等 *即判断两个方案是否相等 * *@param vector<int> board1, vector<int> board2 *@return bool */ bool same_plan(vector<int> board1, vector<int> board2) { //首先判断数组的长度是否相等 if(board1.size() != board2.size()) { return false; } //判断每一个元素是否对应相等 for(int i = 0; i < board1.size(); ++i) { if(board1[i] != board2[i]) { return false; } } //所有元素都比较完毕以后 return true; } /** *In_open()函数,判断一个节点是否在open表中 * *@param pNode, list<pNode> open *@return bool true--在ope表中, false--不再open表中 */ bool in_open(pNode plan, list<pNode> &open) { //遍历整个open表,使用迭代器 for(list<pNode>::iterator it = open.begin(); it != open.end(); it++) { if(same_plan(plan.board, (*it).board)) { return true; } } return false; } /** *in_close()函数,判断一个节点是否在close表中 * *@param pNode, vector<pNode> close *@return bool true 在close表中 false不再表中 */ bool in_close(pNode plan, vector<pNode> &close) { //遍历每一个节点 for(int i = 0; i < close.size(); i++) { if(same_plan(plan.board, close[i].board)) { return true; } } //都遍历完毕以后依然没有找到 return false; } /** *insert_close()函数 将正在访问的节点添加到close表中 * *@param pNode, vector<pNode &close *@return int position ,节点在close表中的索引 */ int insert_close(pNode plan, vector<pNode>& close) { close.push_back(plan); return close.size()-1; } /** *insert_open()函数 将新生成的plan添加到fringe上去 * *@param pNode plan, list<pNode> fringe *@return fringe */ bool insert_open(pNode plan, list<pNode>& open) { //先取得当前方案的f-value值 = h + g; int f = plan.h + plan.g; //int h = plan.h; //遍历fringe,查看此方案是否已在队列中 for(list<pNode>::iterator it = open.begin(); it != open.end(); it++) { //if(h < ((*it).f + (*it).g)) if(f < ((*it).h+(*it).g)) { //cout<<"进行插入"<<endl; open.insert(it, plan); return true; } } //当走到这时,说明这个plan的fvalue是最大的 open.push_back(plan); return true; } /** *generate函数:生成新的解决方案 * *@param vector<int> board, list<board> l, int rc边的长度; */ void generate(list<pNode>& open, vector<pNode> &close, map<vector<int>, int> &allNodes) { //取得h值最小的元素 pNode plan = open.front(); //将取得的节点从open包中删除 open.pop_front(); //将当前方案添加到close表中 int pos = insert_close(plan, close); //数据是3*3的 int rc = plan.rc; //寻找0的位置 int px; int py; for(int i = 0; i < rc*rc; ++i) { if(plan.board[i] == 0) { px = i / rc; py = i % rc; } } //想四个方向拓展,首先得判断能否拓展 for(int i = 0; i < 4; i++) { if(px+x_axis[i] >= 0 && px+x_axis[i] < rc && py+y_axis[i] >= 0 && py+y_axis[i] < rc) { //生成一个新的节点 pNode new_plan = plan; new_plan.board[px*rc+py] = new_plan.board[(px+x_axis[i])*rc+(py+y_axis[i])]; new_plan.board[(px+x_axis[i])*rc+(py+y_axis[i])] = 0; new_plan.h = heuristics(new_plan.board, new_plan.rc); new_plan.g = plan.g+1;//g表示层数 //图搜索关键之处,在哈希中查询新生成的节点是否已经访问过了。 if(allNodes.find(new_plan.board) != allNodes.end()) { //判断这个节点是否比之前的方案更好 if((new_plan.h + new_plan.g) < allNodes[new_plan.board]) { allNodes[new_plan.board] = (new_plan.h + new_plan.g); } else { //如果这个节点并不比以前包含这个节点的方案更好 continue; } } else { allNodes[new_plan.board] = (new_plan.h + new_plan.g); } new_plan.parent = pos;//记录新生成节点的父节点 if(in_open(new_plan, open))//如果新生成的节点在open表中 { for(list<pNode>::iterator it = open.begin(); it != open.end(); it++) { if(same_plan(new_plan.board, (*it).board)) { if((new_plan.h+new_plan.g) > ((*it).h+(*it).g)) { //open.erase(it); break; } else { //删除 open.erase(it); break; } } //找该节点应该插入的位置 bool inserted = false; if(!inserted && (new_plan.h+new_plan.g) < ((*it).h+(*it).g)) { inserted = true; open.insert(it, new_plan); } } } else if(in_close(new_plan, close)) { for(int i = 0; i < close.size(); i++) { if(same_plan(new_plan.board, close[i].board) && ((new_plan.h+new_plan.g)<(close[i].h+close[i].g))) { close[i].h = new_plan.h; close[i].g = new_plan.g; //将这个重新赋值的节点插入到open表中 insert_open(new_plan, open); break; } } } else { insert_open(new_plan, open); } } } return ; } /** *void get_path()函数,根据当前符合目标状态的节点在close表中回溯, * 从而获得从起点到达目标终点的路径 * *@param pNode, vector<pNode> close *@return null */ void get_path(pNode plan, vector<pNode> &close, int plen) { if(plan.parent == -1) { print(plan.board, plan.rc); cout<<"路径长度为:"<<plen++<<endl; return;//递归出口,表示回溯到起点 } //输出当前节点 print(plan.board, plan.rc); return get_path(close[plan.parent], close, ++plen); } int main(int argc, char *argv[]) { //构造输入 pNode plan; //生成初始状态 ifstream data_in("input.txt"); char digit; while(data_in.get(digit)) { if(digit >= '0' && digit <= '9') { int num = digit - '0'; //cout<<digit; plan.board.push_back(num); } } plan.rc = 3; plan.g = 1; plan.parent = -1; //标识起点 plan.h = heuristics(plan.board, plan.rc); //生成open表,并将起点添加到表中 list<pNode> open; open.push_back(plan); //生成记录访问过节点的hashTable,并将起始节点放入到其中 map<vector<int>, int> allNodes; //allNodes.insert(plan.board); allNodes[plan.board] = (plan.h + plan.g); //生成close表 vector<pNode> close; while(!open.empty()) { //print(open.front().board, 3); out_data(open.front()); //判断当前节点是否满足目标状态 if(is_ordered(open.front().board)) { cout<<"************************"<<endl; cout<<"*******查找完成*********"<<endl; cout<<"************************"<<endl; get_path(open.front(), close, 0); break; } //产生新的方案 generate(open, close, allNodes); } //cout<<close.size()<<endl; return 0; }
相关文章推荐
- 搜狗百度360市值齐跌:搜索引擎们陷入集体焦虑?
- 本人即将筹备败家日志,敬请期待!
- 书评:《算法之美( Algorithms to Live By )》
- IE:使用搜索助手
- 动易2006序列号破解算法公布
- Ruby实现的矩阵连乘算法
- C#插入法排序算法实例分析
- 超大数据量存储常用数据库分表分库算法总结
- C#数据结构与算法揭秘二
- C#冒泡法排序算法实例分析
- 算法练习之从String.indexOf的模拟实现开始
- C#算法之关于大牛生小牛的问题
- C#实现的算24点游戏算法实例分析
- c语言实现的带通配符匹配算法
- 浅析STL中的常用算法
- 算法之排列算法与组合算法详解
- C++实现一维向量旋转算法
- Ruby实现的合并排序算法
- C#折半插入排序算法实现方法
- 基于C++实现的各种内部排序算法汇总