利用图搜索来优化八数码问题的A*算法

来源:互联网 发布:java web开发实战入门 编辑:程序博客网 时间:2024/04/27 23:48

在最开始的用A*算法的时候,我们使用树形结构来生成后继的拓展节点,导致我们的解空间是按指数增长,但是实际上我们的路径节点的总个数是一定的,相互链接成了一个图的结构,我们利用树形结构来生成子节点的时候实际上生成了重复的节点。所以图搜索的关键是对生成的重复节点的处理,从而减小了解空间。


下面是算法的为代码:

A*
灰色的区域是利用图搜索技术对算法进行改进。


下面是C++代码:

#include <iostream>#include <fstream>#include <vector>#include <cmath>#include <list>#include <map>//#include "process.cpp";#define SIZE 3//棋盘的大小 size*sizeusing 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;}
0 0
原创粉丝点击