八数码问题

八数码问题有许多种解决方法,深度搜索、宽度搜索、启发式......下面就启发式搜索来解一下八数码问题。

先来说一下基本的原理:

 启发式搜索就是在状态空间中的搜索对每一个搜索的位置进行评估,得到最好的位置,再从这个位置进行搜索直到目标。这样可以省略大量无谓的搜索路径,提高了效率。在启发式搜索中,对位置的估价是十分重要的。采用了不同的估价可以有不同的效果。
 启发中的估价是用估价函数表示的,如:f(n) = g(n) + h(n)
 其中f(n) 是节点n的估价函数,g(n)是在状态空间中从初始节点到n节点的实际代价,h(n)是从n到目标节点最佳路径的估计代价。在这里主要是h(n)体现了搜索的启发信息,因为g(n)是已知的。如果说详细点,g(n)代表了搜索的广度的优先趋势。但是当h(n) >> g(n)时,可以省略g(n),而提高效率。

算法如下:

#include <stdio.h>

#include <stdlib.h>


int total_step = 0;
int open_number;//共遍历了open_number个结点
int end[3][3] = {{1,2,3},
{8,0,4},
{7,6,5}};
typedef struct Eigntnumbers
{
int array[3][3];
int d; //深度
int w; //放错棋子数
int f; //f=d+n;估价函数
int ope; //方向
struct Eigntnumbers * next; //后
struct Eigntnumbers * up; //前 
struct Eigntnumbers * parent;

}Eightnumbers;


Eightnumbers *open,*close;//open表   closed表


int judge(Eightnumbers *p,int end[3][3]); //判断放错棋子的位置
void open_insert(Eightnumbers *head,Eightnumbers *i)//插入到open
{
Eightnumbers *p1,*p2;
p1 = head->next;
p2 = head;
while(p1!=NULL && (i->f >= p1->f))//要插在比结点的w值小的地方
{
p2 = p1;
p1 = p1->next;
}
p2->next = i;
i->up = p2;
i->next = p1;
if(p1!=NULL) //插在p2 p1之间
{
p1->up = i;
}
//printf("f=%4d\n",i->f);


}


int init()//open表初始化
{
int k;
Eightnumbers *p;
open = (Eightnumbers *)malloc(sizeof(Eightnumbers)); //open表初始化
close = (Eightnumbers *)malloc(sizeof(Eightnumbers)); //close表初始化
open->up = open->next = close->up = close->next = NULL;


p=(Eightnumbers *)malloc(sizeof(Eightnumbers));

p->array[0][0] = 3;
p->array[0][1] = 1;
p->array[0][2] = 8;//2,0,3
p->array[1][0] = 7;
p->array[1][1] = 6;
p->array[1][2] = 4;//1,8,4
p->array[2][0] = 0;
p->array[2][1] = 2;
p->array[2][2] = 5;//7,6,5
//k = judge(p,end);
//p->w = k;
p->d = 0;
//p->f = p->w+p->d;
p->parent = NULL;

open_insert(open,p);//初始化,初始状态的结点插入到open
return 1;
}


int judge(Eightnumbers *p,int end[3][3]) //判断放错棋子的位置
{
int count=0;
int a,b;//count标记放错的棋子数;
if(p->array[0][0] != end[0][0])
count++;
if(p->array[0][1] != end[0][1])
count++;
if(p->array[0][2] != end[0][2])
count++;
if(p->array[1][0] != end[1][0])
count++;
if(p->array[1][1] != end[1][1])
count++;
if(p->array[1][2] != end[1][2])
count++;
if(p->array[2][0] != end[2][0])
count++;
if(p->array[2][1] != end[2][1])
count++;
if(p->array[2][2] != end[2][2])
count++;



return count;//除去空的位置
}




Eightnumbers *open_first(Eightnumbers *head) //取 open 表第一个结点,此节点在open表中删除,加入到close表
{
Eightnumbers *p;
//a = p->next;
if(head->next == NULL)
{
return NULL;
}


p = head->next;
head->next = p->next;
if(p->next!=NULL)
{
p->next->up = head;
}
p->up = NULL;
p->next = NULL;
return p;


}
void close_add(Eightnumbers *head,Eightnumbers *i)//结点i加入到closed表中//删除Open表中结点i
{
int a,b;
//Eightnumbers *a;
//结点i加入到closed表中
//a = i->next;
i->next = head->next;
i->up = head;
head->next = i;
if(i->next!=NULL)
{
i->next->up = i;
}


}
int judgenew_open(Eightnumbers *p1,Eightnumbers *open)//判断新扩展的结点在不在open表中
{
int m=0;
Eightnumbers *p;
p = open->next;


while(p != NULL)
{
//判断新扩展的结点在open表中,并且新节点s与p1节点一样

if(p->array[0][0] == p1->array[0][0] && p->array[0][1] == p1->array[0][1] && p->array[0][2] == p1->array[0][2] &&
p->array[1][0] == p1->array[1][0] && p->array[1][1] == p1->array[1][1] && p->array[1][2] == p1->array[1][2] &&
p->array[2][0] == p1->array[2][0] && p->array[2][1] == p1->array[2][1] && p->array[2][2] == p1->array[2][2])
{
m = 1;
break;
}

p = p->next;
}
return m;
}


int judgenew_close(Eightnumbers *p1,Eightnumbers *close)//判断新扩展的结点在不在open closed表中
{
int k=0;
Eightnumbers *j;


j = close->next;
while(j != NULL)
{
//判断新扩展的结点在closed表中,.并且新节点s与j1节点一样

if(j->array[0][0] == p1->array[0][0] && j->array[0][1] == p1->array[0][1] && j->array[0][2] == p1->array[0][2] &&
j->array[1][0] == p1->array[1][0] && j->array[1][1] == p1->array[1][1] && j->array[1][2] == p1->array[1][2] &&
j->array[2][0] == p1->array[2][0] && j->array[2][1] == p1->array[2][1] && j->array[2][2] == p1->array[2][2])
{
k = 1;
break;
}

j = j->next;
}
return k;
}


int show(Eightnumbers *item)
{
    Eightnumbers *p;
int i;
    int step;
    p=item;
    if(p!=NULL)
    {
        step=show(p->parent);
        printf("\nStep %d:\n",step+1);
        for(i=0;i<3;i++)
printf("%4d%4d%4d\n",p->array[i][0],p->array[i][1],p->array[i][2]);
printf("\n");
        return step+1;
    }
    else
    {
        return -1;
    }
}   




void free_list(Eightnumbers *head)
{
    Eightnumbers *p,*q;
    p=head->next;


    while ( p!=NULL )
    {
        q=p->next;
        free(p);
        p=q;
    }
    free(head);


int opera(int i1,int ii,int j1,Eightnumbers *p1)
{
int i=4;
int a1,b1;


//printf("i1=%4d,ii=%4d\n",i1,ii);
if(j1 == 0 && i1!=0)//上
{
a1 = p1->array[i1][ii]; //改变了数值、深度、放错棋子数、f
b1 = p1->array[i1-1][ii];
//printf("1交换前%4d%4d\n",p1->array[i1][ii],p1->array[i1-1][ii]);
p1->array[i1][ii] = b1; //改变了数值、深度、放错棋子数、f
p1->array[i1-1][ii] = a1;
//swap(p1->array[i1][ii],p1->array[i1-1][ii]);
p1->ope = 0;


}
if(j1 == 1 && ii!=0)//左
{
a1 = p1->array[i1][ii]; //改变了数值、深度、放错棋子数、f
b1 = p1->array[i1][ii-1];
//printf("2交换前%4d%4d\n",p2->array[i1][ii],p2->array[i1][ii-1]);
p1->array[i1][ii] = b1; //改变了数值、深度、放错棋子数、f
p1->array[i1][ii-1] = a1;
//printf("2交换后%4d%4d\n",p2->array[i1][ii],p2->array[i1][ii-1]);

}
if(j1 == 2 && i1!=2)//下
{
a1 = p1->array[i1][ii]; //改变了数值、深度、放错棋子数、f
b1 = p1->array[i1+1][ii];
// printf("3交换前%4d%4d\n",p3->array[i1][ii],p3->array[i1+1][ii]);


p1->array[i1][ii] = b1; //改变了数值、深度、放错棋子数、f
p1->array[i1+1][ii] = a1;


//printf("3交换后%4d%4d\n",p3->array[i1][ii],p3->array[i1+1][ii]);
;
}
if(j1 == 3 && ii!=2)//右
{
a1 = p1->array[i1][ii]; //改变了数值、深度、放错棋子数、f
b1 = p1->array[i1][ii+1];

//printf("4交换前%4d%4d\n",p4->array[i1][ii],p4->array[i1][ii+1]);
p1->array[i1][ii] = b1; //改变了数值、深度、放错棋子数、f
p1->array[i1][ii+1] = a1;

}
return 1;
}
int extend(Eightnumbers *p) //扩展结点i,生成其全部结点
{
Eightnumbers *p1;
int i1,ii,flag=0;
int j1,direction=4;
int a1,b1;
int cc,dd;
int w1;
int k1,k2;
int j,ji;


for(i1=0;i1<3;i1++) //查找空格的位置在哪里
{
for(ii=0;ii<3;ii++)
{
if(p->array[i1][ii] == 0)
{
flag=1;break;
}
}
if(flag == 1) //找到了空格位置,即为(i,ii)
break;
}


//printf("i1=%d,ii=%d\n",i1,ii);


//可以从4个方向移动,4个方向依次为:上、左、下、右
for(j1=0;j1<direction;j1++)
{

p1=(Eightnumbers *)malloc(sizeof(Eightnumbers));
p1->d = p->d;
p1->w = p->w;
p1->f = p->f;
for(j=0;j<3;j++)
for(ji=0;ji<3;ji++)
p1->array[j][ji] = p->array[j][ji];


opera(i1,ii,j1,p1);


k1 = judgenew_open(p1,open);
k2 = judgenew_close(p1,close);
//printf("k1=%4d k2=%4d\n\n",k1,k2);
//judge_open_close(p1,close,open,p);
if(k1==0&&k2==0)//调用judegnew函数,判断扩展的新结点在不在open closed表中)
{
p1->parent = p;
p1->d = p1->d+1;
w1 = judge(p1,end);
p1->w = w1;
p1->f = p1->d + w1;
//
//printf("f=%4d\n",p1->w);
if(p1->w == 0) //放错棋子数为0,目标状态达到
{
total_step=show(p1);
printf("共: %d\n",total_step);
free_list(open);
free_list(close);
return 1;//
}
else
{
open_number++;
open_insert(open,p1);
}
}

else
{

free(p1);
}
}
return 0;
}




int main()
{
Eightnumbers *p1;
int i=0,ii=0;
int a,b;
int first[3][3] = {{3,1,8},
{7,6,4},
{0,2,5}};

printf("******************八数码问题******************\n");


printf("*************八数码问题的初始状态*************\n");
for(i=0;i<3;i++)
{
for(ii=0;ii<3;ii++)
printf("%4d",first[i][ii]);
printf("\n");

  • }
printf("************八数码问题的种终止状态************\n");
for(i=0;i<3;i++)
{
for(ii=0;ii<3;ii++)
printf("%4d",end[i][ii]);
printf("\n");
}
printf("\n");

init(); //open表初始化

open_number=1; //open表结点数
p1 = open_first(open); //返回第一项,并从Open表中删除

while(p1!=NULL)
{
close_add(close,p1); //向Close表中插入新节点

        if(extend(p1)) //扩展节点
{
return 1;

}
 
 p1=open_first(open);

}
return 1;
}

你可能感兴趣的:(人工智能,启发式)