A/A*算法解决八数码问题(C++实现)

一、实验原理

1.状态图搜索
1.1搜索树:搜索过程中经过的节点和边按原图的连接关系构成一个树型的有向图,称为搜索树。
1.2搜索方式
树式搜索:记录搜索过程中所经过的所有节点和边
1.3路径的获得
树式搜索:反向求解

2.搜索算法
2.1 CLOSED表和OPEN表
closed表对树式搜索来说存储的是正在成长的搜索树,对线式搜索来说存储的是不断伸长的折线,本身就是所求的路径。
open表存储当前待考查的节点。
2.2树式搜索算法
步1 把初始节点放入OPEN表;
步2 检查OPEN表,若为空,则问题无解,退出;
步3 移出OPEN表中第一个节点N并放入CLOSED表中,并编号为n;
步4 考察节点N是否为目标节点,若是,则搜索成功,退出;
步5 若N不可扩展,则转步2;
步6 扩展节点N,生成所有子节点,对这组子节点作如下处理:
(1)如果有节点N的先辈节点,则删除;
(2)如果有已存在于OPEN表的节点,也删除;但删除之前要比较其返回初始节点的新路径与原路径,如果新路径“短”,则修改这些节点在OPEN表中的原指向父节点的指针,使其指向新的父节点。
(3)如果有已存在于CLOSED表中的节点,则作与(2)同样的处理,并且再将其移出CLOSED表,放入OPEN表重新扩展;
(4)对其余子节点,配上指向父节点n的指针后放入OPEN表,对OPEN表按某种搜索策略排序后转步2。

3.启发函数
用来估计搜索树上节点X与目标节点Sg接近程度的函数,记为h(x)。

4.估价函数
f(x)=g(x)+h(x); 其中g(x)是代价函数,h(x)是启发函数。 或定义为:f(x)=d(x)+h(x); d(x)是x的深度。

二、实验内容

1.定义代价函数G(x)和启发函数H(X),以A算法进行求解。
2.输入初始状态和目标状态。
3.输出从初始状态到目标状态的路线。

三、实验结果

A/A*算法解决八数码问题(C++实现)_第1张图片

四、源代码

//此代码仅可实现简单的八数码问题
//把注释去掉即为A*算法
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

class Node{
      //节点类
    public:
        int Node[3][3];         //每个节点用一个二维数组表示
        class Node *parent;     //节点的先辈节点
        class Node *next;       //节点的扩展节点
};

bool cmp(const Node &s1, const Node &s2){
     //sort函数的排序规则,返回估价函数值最大的节点
    int gx1 = 0, gx2 = 0;//gx为从初始节点到该节点已经付出的代价
    int hx1 = 0, hx2 = 0;//hx为该节点到达目的节点的接近程度估计值
    const Node *ptr1 = &s1;
    const Node *ptr2 = &s2;
    int Node[3][3] = {
     {
     1,2,3},{
     8,0,4},{
     7,6,5}};
    while(ptr1 != NULL){
     
        gx1 += 1;
        ptr1 = ptr1->parent;
    }
    while(ptr2 != NULL){
     
        gx2 += 1;
        ptr2 = ptr2->parent;
    }
    for(int i = 0; i < 3; i++){
     
        for(int j = 0; j < 3; j++){
     
            if(s1.Node[i][j] != Node[i][j]){
     
                hx1 += 1;
            }
            if(s2.Node[i][j] != Node[i][j]){
     
                hx2 += 1;
            }
        }
    }
    return (gx1+hx1) > (gx2+hx2);
}

bool IsEqual(Node n1,Node n2){
     
    for (int i = 0; i < 3;i++)
        for (int j = 0; j < 3;j++)
            if(n1.Node[i][j]!=n2.Node[i][j])
                return false;
    return true;
}
class A_algorithm{
     //A算法搜索
    public:
        A_algorithm(Node *S0, Node *Sg);        //构造函数
        int IsSg(Node *S) {
     return memcmp(&S->Node, &Sg.Node, sizeof(int) * 9);} // 判断是否是目标状态
        int Nextnode(Node *S);                  // 下一个可行的节点
        //int Cantor(int Node[][3]);            // 康托展开
        void algorithm();                       // A算法
        void PrintPath(Node *head);             // 打印路径
        void FreeCLOSED(Node *CLOSED);          // 释放close表
    private:
        //unsigned char allHash[362880];
        Node S0;                                //初始节点
        Node Sg;                                //目标节点
        int Nextnum;
        Node next[4];                           //扩展节点
};
A_algorithm::A_algorithm(Node *S0, Node *Sg){
     
    memcpy(&this->S0.Node, &S0->Node, sizeof(int)*9);
    this->S0.parent = NULL;
    this->S0.next = NULL;
    memcpy(&this->Sg.Node, &Sg->Node, sizeof(int)*9);
    this->Sg.parent = NULL;
    this->Sg.next = NULL;
}
int A_algorithm::Nextnode(Node *S){
     //返回可扩展节点的个数
    Nextnum = 0;
    int posi, posj;
    for (int i = 0; i < 3; i++)
        for (int j=0;j<3;j++)
            if(S->Node[i][j] == 0){
     
                posi = i;posj = j;//定位到S节点的空位置处
                break;
             }
    if(posi-1 >= 0){
     //向上扩展
        Node up = *S;
        up.Node[posi][posj] = up.Node[posi-1][posj];
        up.Node[posi - 1][posj] = 0;
        next[Nextnum] = up;
        next[Nextnum].parent = S;
        Nextnum++;
        /*
        if(allHash[Cantor(up.Node)] == 0){
            next[Nextnum] = up;
            next[Nextnum].parent = S;
            Nextnum++;
        }
        */
    }
    if(posi+1 <= 2){
     //向下扩展
        Node down = *S;
        down.Node[posi][posj] = down.Node[posi+1][posj];
        down.Node[posi + 1][posj] = 0;
        next[Nextnum] = down;
        next[Nextnum].parent = S;
        Nextnum++;
        /*
        if(allHash[Cantor(down.Node)] == 0){
            next[Nextnum] = down;
            next[Nextnum].parent = S;
            Nextnum++;
        }
        */
    }
    if(posj-1 >= 0){
     //向左扩展
        Node left = *S;
        left.Node[posi][posj] = left.Node[posi][posj-1];
        left.Node[posi][posj - 1] = 0;
        next[Nextnum] = left;
        next[Nextnum].parent = S;
        Nextnum++;
        /*
        if(allHash[Cantor(left.Node)] == 0){
            next[Nextnum] = left;
            next[Nextnum].parent = S;
            Nextnum++;
        }
        */
    }
    if(posj+1 <= 2){
     //向右扩展
        Node right = *S;
        right.Node[posi][posj] = right.Node[posi][posj+1];
        right.Node[posi][posj + 1] = 0;
        next[Nextnum] = right;
        next[Nextnum].parent = S;
        Nextnum++;
        /*
        if(allHash[Cantor(right.Node)] == 0){
            next[Nextnum] = right;
            next[Nextnum].parent = S;
            Nextnum++;
        }
        */
    }
    return Nextnum;
}
/*
int A_algorithm::Cantor(int Node[][3]){
    int fac[10] = {1,1,2,6,24,120,720,5040,40320,362880};
    int index = 0;
    for(int i = 7; i >= 0; i--){
        int irow = i / 3, icol = i - i / 3 * 3;
        int count = 0;
        for(int j = 8; j > i; j--){
            int jrow = j / 3, jcol = j - j / 3 * 3;
            if(Node[jrow][jcol] < Node[irow][icol]){
                count++;
            }
        }
        index += (count*fac[8-i]);
    }
    return index;
}
*/
void A_algorithm::algorithm(){
     
    int step = 0;
    //memset(allHash, 0, 362880);//把allHash数组的值全部赋为‘0‘字符
    vector<Node> OPEN;//生成一个node类型的数组OPEN
    Node *CLOSED = new Node;;
    Node *current = CLOSED;
    Node *N;
    OPEN.push_back(S0);//把初始节点S0放入OPEN表
    while(!OPEN.empty()){
     
        N = new Node;
        *N = OPEN[OPEN.size()-1]; OPEN.pop_back();//移出OPEN表的最后一个节点
        step++;
        current->next = N;//把N放入CLOSED表中
        current = current->next;
        if(IsSg(N) == 0){
            //如果目标节点Sg=N,则搜索成功,结束
            PrintPath(N);       //打印出N
            //cout << step<
            FreeCLOSED(CLOSED); //释放CLOSED表
            return;
        }
        int Nextnum = Nextnode(N);//扩展N节点,并取N节点的可扩展节点数
        if(Nextnum == 0)//如果N不可扩展则继续
            continue;
        for(int i = 0; i < Nextnum; i++)
            /*
            for (int j = 0; j < OPEN.size();j++){
                if(IsEqual(OPEN[j], next[i])&&IsEqual(OPEN[j], *N->parent)){

                }
            }
            */
            OPEN.push_back(next[i]);
        sort(OPEN.begin(), OPEN.end(), cmp);//对OPEN表的节点按照估价函数的值进行从大到小排序,每次取估价函数值最小的节点即表中的最后一个节点
        /*
        cout << step << " OPEN TABLE:" << endl;
        for (int i = 0; i < OPEN.size();i++)
            PrintPath(&OPEN[i]);
        */
    }
    cout << "Failed." << endl;
    FreeCLOSED(CLOSED);
    return;
}
void A_algorithm::PrintPath(Node *head){
     
    if(head == NULL){
     
        return;
    }
    else{
     
        PrintPath(head->parent);
        for(int i = 0; i < 3; i++){
     
            for (int j = 0; j < 3; j++) {
     
                if (head->Node[i][j] == 0)
                    cout << "  ";
                else
                    cout << head->Node[i][j]<<" ";
            }
            cout << endl;
        }
        cout <<endl;
    }
}
void A_algorithm::FreeCLOSED(Node *CLOSED){
     
    Node *current;
    while(CLOSED != NULL){
     
        current = CLOSED->next;
        free(CLOSED);
        CLOSED = current;
    }
}
int main() {
     
/*
    Node S0; S0.next = NULL; S0.parent = NULL;
    Node Sg; Sg.next = NULL; Sg.parent = NULL;
    cout<<"Please input initial Node:"<> S0.Node[i][j];
    cout<<"Please input goal Node:"<> Sg.Node[i][j];
*/
    Node S0 = {
     {
     {
     2, 8, 3}, {
     1, 0, 4}, {
     7, 6, 5}}, 0, NULL};//初始化初始节点
    Node Sg = {
     {
     {
     1, 2, 3}, {
     8, 0, 4}, {
     7, 6, 5}}, 0, NULL};//初始化目标节点
    cout << "SearchPath: " << endl<<endl;
    A_algorithm(&S0, &Sg).algorithm();
    return 0;
}

你可能感兴趣的:(笔记)