九宫重排问题

实验内容

用A搜索算法实现重排九宫

实验原理

1).评价函数
以当前状态下各将牌到目标位置的距离之和作为节点的评价标准。距离的定义为:“某将牌行下标与目标位置行下标之差的绝对值 + 列下标与目标位置列下标之差的绝对值”。距离越小,该节点的效果越好。某个状态所有将牌到目标位置的距离之和用“h值”表示。

2).主要函数

(1).countH(state & st);
countH函数功能是计算st状态的h值。
计算过程中将会用到rightPos数组,数组里记录的是目标状态下,0~9每个将牌在九宫格里的位置(位置 = 行下标 * 3 + 列下标)。
(2).f(state * p);
f()=h()+level
(3).look_up_dup(vector<state*> & vec, state * p);
在open表或close表中,是否存在指定状态p,当找到与p完全相等的节点时,退出函数。
(4).search(state & start);
在open表不为空时,按f值由小到大对open表中元素进行排序。
调用findZero()函数找到0值元素的位置。空格可以向上下左右四个方向移动,前提是移动后不能越过九宫格的边界线。确定某方向可走后,空格移动一步,生成状态p’。
此时,检查open表中是否已有p’,若有,更新p’数据;检查close表中是否已有p’,若有,将p’从close表中删除,添加到open表中。
重复的执行这个过程,直到某状态的h值为零。
(5).dump_solution(state * q);
在终端输出解路径。

实验代码与截图

// A*算法
#include<iostream>
#include<vector>
#include<time.h>
#include<algorithm>
using namespace std;

const int GRID = 3;         //Grid表示表格的行数(列数),这是3*3的九宫格
int rightPos[9] = { 4, 0, 1, 2, 5, 8, 7, 6, 3 };    
//目标状态时,若p[i][j]=OMG,那么3*i+j = rightPos[OMG]

struct state{
    int panel[GRID][GRID];
    int level;      //记录深度
    int h;
    state * parent;

    state(int level) :level(level){}

    bool operator == (state & q){   
//判断两个状态是否完全相等(对应位置元素相等),完全相等返回true,否则返回false
        for (int i = 0; i<GRID; i++){
            for (int j = 0; j<GRID; j++){
                if (panel[i][j] != q.panel[i][j])
                    return false;
            }
        }
        return true;
    }

    state & operator = (state & p){ //以状态p为当前状态赋值,对应位置元素相同
        for (int i = 0; i<GRID; i++){
            for (int j = 0; j<GRID; j++){
                panel[i][j] = p.panel[i][j];
            }
        }
        return *this;
    }
};

void dump_panel(state * p){         //将八数码按3*3矩阵形式输出
    for (int i = 0; i<GRID; i++){
        for (int j = 0; j<GRID; j++)
            cout << p->panel[i][j] << " ";
        cout << endl;
    }
}

int countH(state & st){             //给定状态st,计算它的h值。
    int h = 0;
    for (int i = 0; i<GRID; i++){
        for (int j = 0; j<GRID; j++){
            if (st.panel[i][j] != 0)
                h += abs(rightPos[st.panel[i][j]] / GRID - i) +
abs(rightPos[st.panel[i][j]] % GRID - j);       
//h=各个将牌与其目标位置的距离之和.距离定义为:行下标之差的绝对值+列下标之差的绝对值。
        }
    }
    return h;
}

int findZero(state & st){               //找到零值元素,返回其在表中的位置
    for (int i = 0; i<GRID; i++){
        for (int j = 0; j<GRID; j++){
            if (st.panel[i][j] == 0)
                return i * 3 + j;
        }
    }
}

int f(state * p){               //计算并返回f()值,即h值+level
    return countH(*p) + p->level;
}

bool compare_state(state * p, state * q){       //比较两个状态的f值
    return f(p)  >  f(q);
}

vector<state *> open_table;         //open表
vector<state *> close_table;        //close表

vector<state*>::iterator look_up_dup(vector<state*> & vec, state * p){
    vector<state*>::iterator it_r = vec.begin();
    for (; it_r<vec.end(); it_r++){
        if ((*(*it_r)) == *p){
            break;
        }
    }
    return it_r;
}
state * search(state & start){      //A*算法进行搜索
    int level = 0;
    open_table.push_back(&start);
    int count = 0;

    while (!open_table.empty()){
        sort(open_table.begin(), open_table.end(), compare_state);
        //对open表中的元素进行排序

        state * p = open_table.back();
        open_table.pop_back();

        if (countH(*p) == 0)
            return p;           //所有将牌到达目标位置,搜索过程结束
        level = p->level + 1;

        int zeroPos = findZero(*p);
        int x = zeroPos / 3;        //空格的行下标
        int y = zeroPos % 3;        //空格的列下标
        for (int i = 0; i<4; i++){  //上下左右四个方向
            int x_offset = 0, y_offset = 0;
            switch (i){
            case 0:x_offset = 0, y_offset = 1; break;   //右
            case 1:x_offset = 0, y_offset = -1; break;//左
            case 2:x_offset = 1, y_offset = 0; break;//上
            case 3:x_offset = -1, y_offset = 0; break;//下
            };

            if (x + x_offset<0 || x + x_offset >= GRID || y + y_offset<0 || y + y_offset >= GRID){
                continue;       
//若移动一步后,将超出上/下/左/右边界,则这个方向不可走,尝试下一个方向
            }
            state * q = new state(level);       //这个方向可走,扩展下一个节点
            q->parent = p;
            *q = *p;
            q->panel[x][y] = q->panel[x + x_offset][y + y_offset];
            q->panel[x + x_offset][y + y_offset] = 0;//空格沿这个方向移一步

            bool skip = false;
            vector<state *>::iterator dup = look_up_dup(open_table, q);
            //若q已在open表中,则对open表中的信息进行更新
            if (dup != open_table.end()){
                if (f(q) < f(*dup)){
                    (*dup)->level = q->level;
                    (*dup)->parent = q->parent;
                }
                skip = true;
            }

            dup = look_up_dup(close_table, q);
            if (dup != close_table.end()){  //若q已在close表中,且f值比原值小,
                if (f(q) < f(*dup)){            //则将q从close表清除,加入open表
                    delete *dup;
                    close_table.erase(dup);
                    open_table.push_back(q);
                    skip = true;
                }
            }

            if (!skip){
                open_table.push_back(q);
            }
        }

        close_table.push_back(p);
    }
}

void dump_solution(state * q)       //输出解路径
{
    vector<state *> trace;
    while (q){
        trace.push_back(q);
        q = q->parent;
    }

    int count = 0;

    while (!trace.empty()){
        cout << "Step " << count <<endl;
        dump_panel(trace.back());
        cout << "h: " << countH(*trace.back()) <<"\tg:"<<count<< "\tf: " << f(trace.back()) << endl;
        cout<<"------------------------------\n\n";
        trace.pop_back();
        count++;
    }
}
int main()
{
    state p(0);
    state *q;

    p.panel[0][0] = 2;//设置初始状态
    p.panel[0][1] = 8;
    p.panel[0][2] = 3;
    p.panel[1][0] = 1;
    p.panel[1][1] = 6;
    p.panel[1][2] = 4;
    p.panel[2][0] = 7;
    p.panel[2][1] = 0;
    p.panel[2][2] = 5;

    p.parent = NULL;
    q = search(p);
    dump_solution(q);
    system("pause");
}

这里写图片描述


总结

启发式搜索是在搜索中加入了与问题有关的启发性信息,用以指导搜索朝着最有希望的方向前进,加速问题的求解过程并找到最优解。A* 算法实际上就是一种启发式搜索,利用一个估价函数评估每次的的决策的价值,决定先尝试哪一种方案,这样可以极大的优化普通的广度优先搜索。在启发式搜索中,对位置的估价是十分重要的。采用了不同的估价可以有不同的效果。启发中的估价是用估价函数表示的,如:f(n) = g(n) + h(n)其中f(n)是节点n的估价函数,g(n)是在状态空间中从初始节点到n节点的实际代价,h(n)是从n到目标节点最佳路径的估计代价。在此九宫问题中,显然g(n)就是从初始状态变换到当前状态所移动的步数,估计函数f(n)我们就可采用当前状态各个数字牌不在目标状态未知的个数,即错位数。


2017.4.28周五下午续——第二种方案

#define N 3
#include "stdlib.h"
#include "stdio.h"
static int B[N][N]={{1,2,3},{8,0,4},{7,6,5}};
typedef struct node{ 
    int s[N][N];
    int value;
    int parent;
}state;

state open[100],close[100];
int f,r,f1,r1;

void change(int a[N][N],state b[],int l){ 
    int i,j;
    for(i=0;i<N;i++)
    for(j=0;j<N;j++)
    a[i][j]=b[l].s[i][j];

}

int com(int A[N][N],int C[N][N]){ 
    int i,j,a;
    a=0;
    for(i=0;i<N;i++)
    for(j=0;j<N;j++)
    if(A[i][j]!=C[i][j])  a++;
    return(a);
}

int depth(state A[],int i){ 
    int a;
    a=0;
    while(i!=0){ 
        i=A[i].parent;   
        a++;
    }
    return(a+1);
}

void sort(){ 
    int i,j,k,l,l1,l2;
    state A;
    for(i=f;i<r-1;i++){ 
        k=i;
        for(j=i+1;j<r;j++){
            if(open[j].value<open[k].value){
                k=j;    
            }           
        }

        for(l1=0;l1<N;l1++){
            for(l2=0;l2<N;l2++){ 
                A.s[l1][l2]=open[k].s[l1][l2];
                open[k].s[l1][l2]=open[i].s[l1][l2];
                open[i].s[l1][l2]=A.s[l1][l2];             
            }       
        }

        A.value=open[k].value;  
        open[k].value=open[i].value;
        open[i].value=A.value;
        A.parent=open[k].parent;
        open[k].parent=open[i].parent;
        open[i].parent=A.parent;
        for(l=f;l<r;l++){
            if(open[l].parent==k){
                open[l].parent=i;
            }       
        }
    }
}

void heuristic_search1(int A[N][N]){ 
    int i,j,k,l,l1,l2,val;
    i=j=0;
    while(i<r){
        if(com(A,open[i].s)!=0){
            i++;        
        }  
        else break;
    }       

    while(j<r1){
        if(com(A,close[i].s)!=0){
            j++;
        }  
        else break;
    }

    if(i>=r&&j>=r1){ 
        val=com(A,B)+depth(open,f-1)+1;
        for(l1=0;l1<N;l1++)
        for(l2=0;l2<N;l2++)
        open[r].s[l1][l2]=A[l1][l2];
        open[r].value=val;
        open[r].parent=f-1;
        r++;
    }
    else if(i<r){ 
        if(depth(open,f-1)+1<depth(open,i))
        { 
            r++;

            for(l1=0;l1<N;l1++)
            for(l2=0;l2<N;l2++){
                open[i].s[l1][l2]=A[l1][l2];
                open[i].value=val;
                open[r].parent=f-1;
            }           
        }
    }
    else { 
        if(depth(close,f1)+1<depth(close,j)){ 
            r++;
            for(l1=0;l1<N;l1++)
            for(l2=0;l2<N;l2++)
            open[r].s[l1][l2]=A[l1][l2];
            open[r].value=val;
            open[r].parent=f-1;
            for(l=j;l<r1;l++)
            { 
                for(l1=0;l1<N;l1++)                           
                for(l2=0;l2<N;l2++){
                    close[l].s[l1][l2]=close[l+1].s[l1][l2];
                    close[l].value=close[l+1].value;        
                    close[l].parent=close[l+1].parent;
                    r1--;   
                }           
            }        
        }
    }
}

void heuristic_search(int search[N][N])
{ 
    int i,j,k,A[N][N],h,b,c,d,e,val;
    int l1,l2,road[20];
    for(i=0;i<N;i++)
    for(j=0;j<N;j++)
    { 
        open[0].s[i][j]=search[i][j];
        close[0].s[i][j]=0;
    }

    open[0].value=depth(open,0)+com(open[0].s,B);
    open[0].parent=-1;
    f=f1=r1=0;  r=1;
    while(f!=r)
    { 
        e=f;
        for(i=0;i<N;i++)
        for(j=0;j<N;j++)
        close[r1].s[i][j]=open[f].s[i][j];
        close[r1].value=open[f].value;
        close[r1].parent=open[f].parent;
        r1++;
        f++;
        change(A,open,e);
        if(com(A,B)==0)
        { 
            printf("\n  success!");
            k=e;
            l1=0;
            while(k!=-1){ 
                road[l1]=k;
                l1++;
                k=open[k].parent;

            }
            for(k=l1;k>=0;k--){ 
            printf("\tStep %d\n",l1-k);
            for(i=0;i<N;i++){ 
                for(j=0;j<N;j++)
                if(open[road[k]].s[i][j]!=0)  
                printf("\t%d",open[road[k]].s[i][j]);
                else printf("\t ");
                printf("\n"); 
            }
            }
            return;
        }
        for(i=0;i<N;i++){ 
            for(j=0;j<N;j++)
            if(A[i][j]==0) 
            break;
            if(j<N) break;
        }
        if(i+1<N)
        { 
            b=A[i+1][j];
            A[i+1][j]=A[i][j];
            A[i][j]=b;
            heuristic_search1(A); 
        }
        change(A,open,e);
        if(i-1>-1){ 
            b=A[i-1][j];
            A[i-1][j]=A[i][j];
            A[i][j]=b;
            heuristic_search1(A);
        }
        change(A,open,e);
        if(j+1<N){ 
            b=A[i][j+1];
            A[i][j+1]=A[i][j];
            A[i][j]=b;
            heuristic_search1(A); 
        }
        change(A,open,e);
        if(j-1>-1){ 
            b=A[i][j-1];
            A[i][j-1]=A[i][j];
            A[i][j]=b;
            heuristic_search1(A); 
        }
        change(A,open,e);
        sort();
    }
    printf("\n   failure");
}

int main()
{ 
    int A[N][N],i,j;
    printf("please input the original state:\n");
    for(i=0;i<N;i++)
    for(j=0;j<N;j++)
    scanf("%d",&A[i][j]);
    heuristic_search(A);
    return 0;
}

  • 5
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值