15数码问题(A*算法)

版权声明:本文为博主原创文章,未经博主允许不得转载哦!!! https://blog.csdn.net/lph188/article/details/86504502

一、题目

 设计一个启发函数,利用A*算法求解15数码问题。

初始状态
5 1 2 4
9 6 3 8
13 15 10 11
14 0 7 12
目标状态
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 0

要求: 尽可能用与A*算法一致的思路实现算法, 力求简单明了地给出一个解.

二、问题描述

15数码问题其实就是棋盘移动问题,是人工智能中的一个经典案例。如上图所示,在一个4 X 4的16宫格棋盘上,有15个数字将牌(整数1~15),还有一个空格(为了方便操作用数字0表示);空格周围四个方向的将牌允许向空格移动,通过移动将牌就可以改变将牌棋盘的格局。现在,我们要解决的问题就是如何移动将牌使初始状态成为目标状态。

三、算法描述

 在这里,我们将使用的是启发式搜索算法:A*算法。在使用A*算法之前,我们先来了解A算法。

启发式搜索算法A,一般简称为A算法,是一种典型的启发式搜索算法。其基本思想是:定义一个评价函数f,对当前的搜索状态进行评估,找出一个最有希望的节点来扩展。

评价函数的形式如下:
                      f(n)=g(n)+h(n)

其中n是被评价的节点。

我们先来定义下面几个函数的含义,它们与f(n)、g(n)和h(n)的差别是都带有一个"*"号。

g*(n):表示从初始节点s到节点n的最短路径的耗散值;

h*(n):表示从节点n到目标节点g的最短路径的耗散值;

f*(n)=g*(n)+h*(n):表示从初始节点s经过节点n到目标节点g的最短路径的耗散值。
     而f(n)、g(n)和h(n)则分别表示是对f*(n)、g*(n)和h*(n)三个函数值的的估计值。是一种预测。A算法就是利用这种预测,来达到有效搜索的目的的。它每次按照f(n)值的大小对OPEN表中的元素进行排序,f值小的节点放在前面,而f值大的节点则被放在OPEN表的后面,这样每次扩展节点时,都是选择当前f值最小的节点来优先扩展。

当在算法A的评价函数中,使用的启发函数h(n)是处在h*(n)的下界范围,即满足h(n)<=h*(n)时,把这个算法就称为A*算法。

四、具体步骤

(1).把起始结点放到OPEN表中。计算F(S),并把其值与结点S联系起来。

(2).如果OPEN表是个空表,则没有解,失败退出;否则继续。

(3).从OPEN表中选择一个F值最小的结点I。如果有几个结点合格,当其中有一个为目标结点时,则选择此目标结点,否则就选择其中任一个结点为结点I。

(4).把结点I从OPEN表中移出,并把它放入CLOSE的扩展结点表中。

(5).如果I是目标结点,则成功退出,求得一个解。

(6).扩展结点I,生成其全部后继结点。对于I的每一个后继结点J:

       (a).计算F(J).

       (b).如果J既不在OPEN表中,也不在CLOSE表中,则用估价函数F把它添入OPEN表中。从J加一指向其父辈结点I的指针,以便一旦找到目标结点时记住一个解答捷径。

       (c).如果J已在OPEN表或CLOSE表上,则比较刚刚对J计算过的F值和前面计算过的该结点在表中的F值。如果新的F值较小,则

             (i).以此新值代替旧值。

             (ii).从J指向I,而不是指向它的父辈结点。

             (iii).如果结点J在CLOSE表中,则把它移回OPEN表中。

(7).转向(2),即GOTO(2)

五、程序实现

首先,我们要建立一个结构体来存放信息:棋盘数据、移动规则、空格位置、是否遍历标志、f函数值、深度、父亲结点、儿子结点。

typedef struct QNode

{ int data[N][N]; //数据

    int ancent;     //标记方向左上右下分别为 1234 ,5为可以任意方向

    int x;          //标记0的横坐标

    int y;          //标记0的纵坐标

    int gone;       //是否遍历该节点,0未遍历,1遍历过

    int value;      //和目标的状态差=不在位将牌距离和+深度

    int deep;       //深度

    struct QNode *father;    //存放前一节点在"store"数组中的位置

    struct QNode *next;      //存放下一节点在"store"数组中的位置

}QNode, *QueuePtr;

接下来,我们要定义几个工具函数:

bool begin_opint():判断数据中是否有数字0(空格),即判断是否为正规数据;

bool compare(int a[N][N]):比较函数,判断当前数据状态是否与目标状态相同;

bool moveleft(int a[N][N], QueuePtr *b, int x, int y):数字0(空格)向左移动函数;

bool moveup(int a[N][N], QueuePtr *b, int x, int y):数字0(空格)向上移动函数;

bool moveright(int a[N][N], QueuePtr *b, int x, int y) :数字0(空格)向右移动函数;

bool movedown(int a[N][N], QueuePtr *b, int x, int y):数字0(空格)向下移动函数;

void output(QueuePtr *p):输出函数;

int getvalue(QueuePtr *p):计算耗散值函数,深度值+不在位将牌距离和。

然后,在主函数中利用这些函数使空格按照规则进行移动,边移动边比较目标状态,最后输出结果。

六、运行结果 

 

七、源代码 

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#define N 4
typedef struct QNode{
	int data[N][N]; //数据
	int ancent;	  //标记方向左上右下分别为 1234 ,5为可以任意方向
	int x;          //标记0的横坐标
	int y;          //标记0的纵坐标
	int gone;		  //是否遍历该节点,0未遍历,1遍历过
	int value;		  //和目标的状态差=不在位将牌距离和+深度
	int deep;       //深度
	struct QNode *father;	//存放前一节点在"store"数组中的位置
	struct QNode *next;      //存放下一节点在"store"数组中的位置
}QNode, *QueuePtr;
typedef struct{
	QueuePtr head; //头结点
	QueuePtr rear; //尾结点
}LinkQueue;
int A[N][N]={          //目标状态
			 {1,2,3,4},
			 {5,6,7,8},
			 {9,10,11,12},
			 {13,14,15,0}			
			};
int B[N][N]={          //初始状态
			 {5,1,2,4},
			 {9,6,3,8},
			 {13,15,10,11},
			 {14,0,7,12}			
			};
int x,y;
QueuePtr min;     //存放最小的结点
bool begin_opint(){ //判断输入的数据是否合法
	int i,j;
	for(i=0;i<N;i++){
		for(j=0;j<N;j++){
			if(A[i][j]==0){
				x=i;y=j;
				return true;}
		}
	}
	return false;
}
bool compare(int a[N][N]){ //比较函数,判断当前状态是否与目标状态相等
	int i,j;
	for(i=0;i<N;i++){
		for(j=0;j<N;j++){
			if(a[i][j]!=B[i][j])
				return false;
		}
	}
	return true;
}
bool moveleft(int a[N][N],QueuePtr *b,int x,int y){//向左移动函数
	int k,i,j;
	if(y==0)
		return false;
	for(i=0;i<N;i++){
		for(j=0;j<N;j++)
			(*b)->data[i][j]=a[i][j];
	}
	k=(*b)->data[x][y];
	(*b)->data[x][y]=(*b)->data[x][y-1];
	(*b)->data[x][y-1]=k;
	(*b)->x=x;
	(*b)->y=y-1;
    return true;
}
bool moveup(int a[N][N],QueuePtr *b,int x,int y){//向上移动函数
	int k,i,j;
	if(x==0)
		return false;
	for(i=0;i<N;i++){
		for(j=0;j<N;j++)
			(*b)->data[i][j]=a[i][j];
	}
	k=(*b)->data[x][y];
	(*b)->data[x][y]=(*b)->data[x-1][y];
	(*b)->data[x-1][y]=k;
	(*b)->x=x-1;
	(*b)->y=y;
	return true;
}
bool movedown(int a[N][N],QueuePtr *b,int x,int y){ //向下移动函数
	int k,i,j;
	if(x==N-1)return false;
	for(i=0;i<N;i++){
		for(j=0;j<N;j++)
			(*b)->data[i][j]=a[i][j];
	}
	k=(*b)->data[x][y];
	(*b)->data[x][y]=(*b)->data[x+1][y];
	(*b)->data[x+1][y]=k;
	(*b)->x=x+1;
	(*b)->y=y;
	return true;
}
bool moveright(int a[N][N],QueuePtr *b,int x,int y){ //向右移动函数
	int k,i,j;
	if(y==N-1)
		return false;
	for(i=0;i<N;i++){
		for(j=0;j<N;j++)
			(*b)->data[i][j]=a[i][j];
	}
	k=(*b)->data[x][y];
	(*b)->data[x][y]=(*b)->data[x][y+1];
	(*b)->data[x][y+1]=k;
	(*b)->x=x;
	(*b)->y=y+1;
    return true;
}
bool copy(QueuePtr *a){ //复制函数
	int i,j;
	for(i=0;i<N;i++){
		for(j=0;j<N;j++)
			(*a)->data[i][j]=A[i][j];
	}
	return true;
}
void output(QueuePtr *p){ //输出函数
	int i,j;
	long int n=0;
	for(;(*p)->father!=NULL;(*p)=(*p)->father,n++){
		for(i=0;i<N;i++){
			for(j=0;j<N;j++){
				printf(" %d",(*p)->data[i][j]);
			}printf("\n");
		}printf("\n");
	}
	printf("step is %d\n",n-1);
}
int getvalue(QueuePtr *p){ //计算耗散值函数
	int count=0;//保存距离
	bool test=true;  //若已找到一个位置的值则继续找下一个
	//计算不在位的距离和
	for(int i=0;i<N;i++){
		for(int j=0;j<N;j++){
			test=true;
			for(int k=0;k<N;k++){
				for(int l=0;l<N;l++){
					if((i!=(*p)->x||j!=(*p)->y)&&(*p)->data[i][j]==B[k][l]){
						count=count+abs(i-k)+abs(j-l);
						test=false;
					}
					if(test==false) break;
				}
				if(test==false) break;
			}
		}
	}
	count=count+(*p)->deep;//加上深度值
	return count;
}
void main()
{
	QueuePtr closed,p,q;
	LinkQueue open;
	if(!begin_opint()){
		printf("no 0 opint!!\n");	//确定0点
		exit(0);
	}
	open.head=open.rear=(QueuePtr)malloc(sizeof(QNode));//头结点
	open.head->father=NULL;
	open.rear->next=open.head->next=NULL;
	closed=(QueuePtr)malloc(sizeof(QNode));//头结点
	closed->next=NULL;
	closed->father=NULL;
	p=(QueuePtr)malloc(sizeof(QNode));//S0进open表
	copy(&p);
	p->x=x;
	p->y=y;
	p->ancent=5;
	p->deep=0;			//s0的深度为0
	p->gone=0;
	p->father=open.head;
	p->value=getvalue(&p);						
	p->next=open.head->next;
	open.head->next=p;
	open.rear=open.head;
	if(compare(p->data)){
		output(&p);
		exit(0);
	}
	while(open.head->next!=NULL){
		//寻找最小状态
		for(min=q=open.head->next;q!=NULL;q=q->next){
			if(q->value<=min->value&&q->gone==0){
				min=q;
				break;
			}
		}
		min->gone=1;     //改最小状态已遍历
		min->father->next=min->next;  //在open表中删除找到的最小态
		min->next=closed->next;		//插入closed表的表头			
		closed->next=min;
		//空格向4个方向移动
		switch(closed->next->ancent){
			case 1:p=(QueuePtr)malloc(sizeof(QNode));//祖先结点从右来	if(moveleft(closed->next->data,&p,closed->next->x,closed->next->y)){
							p->father=closed->next;
							p->ancent=1;
							p->gone=0;
							p->deep=min->deep+1;
							p->value=getvalue(&p);
							p->next=open.rear->next;
							open.rear->next=p;
							open.rear=p;
							//比较输出结果
							if(compare(p->data)){
								output(&p);
								exit(0);
							}			
					}else free(p);
					p=(QueuePtr)malloc(sizeof(QNode));
			if(moveup(closed->next->data,&p,closed->next->x,closed->next->y)){
							p->father=closed->next;
							p->ancent=2;
							p->gone=0;
							p->deep=min->deep+1;
							p->value=getvalue(&p);
							p->next=open.rear->next;
							open.rear->next=p;
							open.rear=p;
							//比较输出结果
							if(compare(p->data)){
								output(&p);
								exit(0);
							}					
					}else free(p);
					p=(QueuePtr)malloc(sizeof(QNode));
					if(movedown(closed->next->data,&p,closed->next->x,closed->next->y)){
							p->father=closed->next;
							p->ancent=3;
							p->gone=0;
							p->deep=min->deep+1;
							p->value=getvalue(&p);
							p->next=open.rear->next;
							open.rear->next=p;
							open.rear=p;
						   	//比较输出结果
							if(compare(p->data)){
								output(&p);
								exit(0);
							}				
					}else free(p);
				break;
			case 2:p=(QueuePtr)malloc(sizeof(QNode));//祖先结点从下来			if(moveleft(closed->next->data,&p,closed->next->x,closed->next->y)){
							p->father=closed->next;
							p->ancent=1;
							p->gone=0;
							p->deep=min->deep+1;
							p->value=getvalue(&p);
							p->next=open.rear->next;
							open.rear->next=p;
							open.rear=p;
							//比较输出结果
							if(compare(p->data)){
								output(&p);
								exit(0);
							}										
					}else free(p);
					p=(QueuePtr)malloc(sizeof(QNode));
	if(moveup(closed->next->data,&p,closed->next->x,closed->next->y)){
							p->father=closed->next;
							p->ancent=2;
							p->gone=0;
							p->deep=min->deep+1;
							p->value=getvalue(&p);
							p->next=open.rear->next;
							open.rear->next=p;
							open.rear=p;
							//比较输出结果
							if(compare(p->data)){
								output(&p);
								exit(0);
							}						
					}else free(p);
					p=(QueuePtr)malloc(sizeof(QNode));
	if(moveright(closed->next->data,&p,closed->next->x,closed->next->y)){
							p->father=closed->next;
							p->ancent=4;
							p->gone=0;
							p->deep=min->deep+1;
							p->value=getvalue(&p);
							p->next=open.rear->next;
							open.rear->next=p;
							open.rear=p;
							//比较输出结果
							if(compare(p->data)){
								output(&p);
								exit(0);
							}								
					}else free(p);
				break;
			case 3:p=(QueuePtr)malloc(sizeof(QNode));//祖先结点从上来			if(moveleft(closed->next->data,&p,closed->next->x,closed->next->y)){
							p->father=closed->next;
							p->ancent=1;
							p->gone=0;
							p->deep=min->deep+1;
							p->value=getvalue(&p);
							p->next=open.rear->next;
							open.rear->next=p;
							open.rear=p;
							//比较输出结果
							if(compare(p->data)){
								output(&p);
								exit(0);
							}							
					}else free(p);
					p=(QueuePtr)malloc(sizeof(QNode));
	if(movedown(closed->next->data,&p,closed->next->x,closed->next->y)){
							p->father=closed->next;
							p->ancent=3;
							p->gone=0;
							p->deep=min->deep+1;
							p->value=getvalue(&p);
							p->next=open.rear->next;
							open.rear->next=p;
							open.rear=p;
							//比较输出结果
							if(compare(p->data)){
								output(&p);
								exit(0);
							}							
					}else free(p);
					p=(QueuePtr)malloc(sizeof(QNode));
	if(moveright(closed->next->data,&p,closed->next->x,closed->next->y)){
							p->father=closed->next;
							p->ancent=4;
							p->gone=0;
							p->deep=min->deep+1;
							p->value=getvalue(&p);
							p->next=open.rear->next;
							open.rear->next=p;
							open.rear=p;
							//比较输出结果
							if(compare(p->data)){
								output(&p);
								exit(0);
							}								
					}else free(p);
				break;
			case 4:p=(QueuePtr)malloc(sizeof(QNode));//祖先结点从左边来
	if(moveup(closed->next->data,&p,closed->next->x,closed->next->y)){
							p->father=closed->next;
							p->ancent=2;
							p->gone=0;
							p->deep=min->deep+1;
							p->value=getvalue(&p);
							p->next=open.rear->next;
							open.rear->next=p;
							open.rear=p;
							//比较输出结果
							if(compare(p->data)){
								output(&p);
								exit(0);
							}						
					}else free(p);
					p=(QueuePtr)malloc(sizeof(QNode));
					if(movedown(closed->next->data,&p,closed->next->x,closed->next->y)){
							p->father=closed->next;
							p->ancent=3;
							p->gone=0;
							p->deep=min->deep+1;
							p->value=getvalue(&p);
							p->next=open.rear->next;
							open.rear->next=p;
							open.rear=p;
							if(compare(p->data)){ //比较输出结果
								output(&p);
								exit(0);
							}							
					}else free(p);
					p=(QueuePtr)malloc(sizeof(QNode));
		if(moveright(closed->next->data,&p,closed->next->x,closed->next->y)){
							p->father=closed->next;
							p->ancent=4;
							p->gone=0;
							p->deep=min->deep+1;
							p->value=getvalue(&p);
							p->next=open.rear->next;
							open.rear->next=p;
							open.rear=p;
							if(compare(p->data)){ //比较输出结果
								output(&p);
								exit(0);
							}							
					}else free(p);
				break;
			default:p=(QueuePtr)malloc(sizeof(QNode));//初始情况
		if(moveleft(closed->next->data,&p,closed->next->x,closed->next->y)){
							p->father=closed->next;
							p->ancent=1;
							p->gone=0;
							p->deep=min->deep+1;
							p->value=getvalue(&p);
							p->next=open.rear->next;
							open.rear->next=p;
							open.rear=p;
							//比较输出结果
							if(compare(p->data)){
								output(&p);
								exit(0);
							}						
					}else free(p);
					p=(QueuePtr)malloc(sizeof(QNode));					if(moveup(closed->next->data,&p,closed->next->x,closed->next->y)){
							p->father=closed->next;
							p->ancent=2;
							p->gone=0;
							p->deep=min->deep+1;
							p->value=getvalue(&p);
							p->next=open.rear->next;
							open.rear->next=p;
							open.rear=p;
							if(compare(p->data)){ //比较输出结果
								output(&p);
								exit(0);
							}								
					}else free(p);
					p=(QueuePtr)malloc(sizeof(QNode));			if(movedown(closed->next->data,&p,closed->next->x,closed->next->y)){
							p->father=closed->next;
							p->ancent=3;
							p->gone=0;
							p->deep=min->deep+1;
							p->value=getvalue(&p);
							p->next=open.rear->next;
							open.rear->next=p;
							open.rear=p;
							if(compare(p->data)){ //比较输出结果
								output(&p);
								exit(0);
							}							
					}else free(p);
					p=(QueuePtr)malloc(sizeof(QNode));
	if(moveright(closed->next->data,&p,closed->next->x,closed->next->y)){
							p->father=closed->next;
							p->ancent=4;
							p->gone=0;
							p->deep=min->deep+1;
							p->value=getvalue(&p);
							p->next=open.rear->next;
							open.rear->next=p;
							open.rear=p;
							if(compare(p->data)){ //比较输出结果
								output(&p);
								exit(0);
							}						
					}else free(p);
				break;
		}
	}printf("error: no answer!\n");
}

猜你喜欢

转载自blog.csdn.net/lph188/article/details/86504502
今日推荐