A*算法的简单实现(八数码问题)


第一部分:A*算法简介

程序环境:

运行环境:Windows xp

使用工具:Vc++6.0

启发式搜索简介:

启发式搜索就是在状态空间中的搜索对每一个搜索的位置进行评估,得到最好的位置,再从这个位置进行搜索直到目标。这样可以省略大量无谓的搜索路径,提高了效率。在启发式搜索中,对位置的估价是十分重要的。采用了不同的估价可以有不同的效果。 

它把到达节点的耗散g(n)和从该节点到目标节点的消耗h(n)结合起来对节点进行评价:f(n)=g(n)+h(n) 

A*算法简介:

A*算法就是对估价函数加上一些限制后得到的一种启发式搜索算法。

搜索过程可能描述如下:

1)把初始节点S0放入Open表中,f(S0)=g(S0)+h(S0)

2)如果Open表为空,则问题无解,失败退出;

3)把Open表的第一个节点取出放入Closed表,并记该节点为n

4)考察节点n是否为目标节点。若是,则找到了问题的解,成功退出;

5)若节点n不可扩展,则转到第(2)步;

6)扩展节点n,生成子节点ni(i=1,2,……),计算每一个子节点的估价值f(ni) (i=1,2,……),并为每一个子节点设置指向父节点的指针,然后将这些子节点放入Open表中;

7)根据各节点的估价函数值,对Open表中的全部节点按从小到大的顺序重新进行排序;

8)转第(2)步。

     
   

第二部分:A*算法的简单实现(八数码问题)

图解:





代码:

如若代码有问题或有更为严重的错误请不吝赐教。

//八数码(九宫格)、拼图游戏???
/*==============================================================================
	八数码问题的未找到路径时的条件是什么?

	是否为 OPEN 表为空时还未找到路径即为无解条件

	如此的话,运算量太大(此题最大为9!个),本机配置根本无法运行到最后-_-!(所以不知道是否正确)

==============================================================================*/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#define MAX_NODESIZE 362880	//9的阶乘9!=362880

typedef struct node{		//八数码结构体

	int a[3][3];//节点状态

	//空格下标
	int i_0;		
	int j_0;

	//启发信息
	int d;		//搜索深度
	int w;		//各棋子不在正确位置的数目
	int f;		//总代价

	struct node *father;	//指向父节点指针

}node,*p_node;


typedef struct list	//顺序表结构体
{
	p_node a[MAX_NODESIZE];
	long length;

}list,*p_list;

//static int s0[3][3]={8,2,3,1,0,4,7,6,5};	//初始结点

static int s0[3][3]={2,8,3,1,0,4,7,6,5};	//初始结点
/*
2	8	3
1	0	4
7	6	5
*/

static int sg[3][3]={1,2,3,8,0,4,7,6,5};	//目标结点
/*
1	2	3
8	0	4
7	6	5
*/

p_node s_0=(p_node) malloc(sizeof(node));	//初始节点
p_node s_g=(p_node) malloc(sizeof(node));	//目标节点

p_list OPEN  =(p_list) malloc (sizeof(list));	//OPEN表
p_list CLOSED=(p_list) malloc (sizeof(list));	//CLOSE表


int w(p_node s);						//计算各棋子不在正确位置的数目
int f(p_node s);						//估价函数
void init_node();						//初始化
void out_node(p_node s);				//输出八数码
void list_reverse(p_node &p);			//将链表逆序
void out_list(p_list &l);				//输出OPEN表
bool search_list(p_list &l,p_node s);	//对表进行查找,成功返回true
void sort_list(p_list &l);				//对OPEN表进行排序(按f从小到大)
void add_list(p_list &l,p_node s);		//加入结点到OPEN表中或CLOSE表中
void copy_node(p_node s1,p_node &s2);	//生成新的结点(将s2赋值给s1)
void delete_list(p_list &l);			//从OPEN表或CLOSE中删除结点
bool is_equal(p_node s1,p_node s2);		//判断两节点是否相等
bool up_mov(p_node &s);					//空格上移
bool down_mov(p_node &s);				//空格下移
bool left_mov(p_node &s);				//空格左移
bool right_mov(p_node &s);				//空格右移
void move(p_node s);					//移动父节点并加入未探索表中(扩展结点)


int main()
{
	init_node();
	
	printf("\n程序求解过程如下:\n");
	printf("=========================================================\n\n");

	while(OPEN->length!=0 && CLOSED->length<=1000)	//最多循环次数1000
	{

		p_node n=OPEN->a[0];		//---------------把Open表的第一个节点取出放入Closed表,并记该节点为n
		delete_list(OPEN);

		if(is_equal(n,s_g))	//考察节点n是否为目标节点。若是,则找到了问题的解,成功退出;亦可换成 if(w(n)==0){...}
		{
			list_reverse(n);
			list_reverse(n);
			list_reverse(n);

			while(n)
			{
				printf("第 %d 步:\n",n->d+1);
				out_node(n);
				n=n->father;
			}

			break;
		}
	
		add_list(CLOSED,n);

		move(n);//扩展结点n
		
		sort_list(OPEN);
	//	out_list(OPEN);
	}

	if(OPEN->length==0 || CLOSED->length>1000)
	{
		printf("\n从初始结点无法到达目标结点!\n\n");	
	}

	return 0;
}

int f(p_node s)	//估价函数
{
	return (s->d+s->w);
}


void out_node(p_node s)	//输出八数码
{
	
	printf("-------------------");
	printf(" x=%d,y=%d\n",s->i_0,s->j_0);
	for (int i=0;i<3;i++)
	{
		for (int j=0;j<3;j++)
		{
			printf("%5d",s->a[i][j]);
		}

		printf("\n");
	}
	printf("-------------------");
	printf(" d=%d,w=%d;  f=%d\n\n\n",s->d,s->w,s->f);	

}


void out_list(p_list &l)		//输出OPEN表
{
	
	printf("****************************************************************\n");
	for(int i=0;i<l->length;i++)
	{
		out_node(l->a[i]);
	}
	printf("****************************************************************\n");
}


int w(p_node s)		//计算各棋子不在正确位置的数目
{
	int w=0;

	for (int i=0;i<3;i++)
	{
		for (int j=0;j<3;j++)
		{
			if (s->a[i][j]!=sg[i][j])
			{
				w++;
			}
		}
	}

	if (s->a[1][1]==sg[1][1])
	{
		w+=1;
	}

	return w-1;
}

bool left_mov(p_node &s)		//空格左移
{
	int x=s->i_0,y=s->j_0;

	if (y==0)
	{
		return false;
	}

	int t;
	t=s->a[x][y];
	s->a[x][y]=s->a[x][y-1];
	s->a[x][y-1]=t;

	--s->j_0;

	return true;
}

bool right_mov(p_node &s)	//空格右移
{
	int x=s->i_0,y=s->j_0;

	if (y==2)
	{
		return false;
	}

	int t;
	t=s->a[x][y];
	s->a[x][y]=s->a[x][y+1];
	s->a[x][y+1]=t;
	
	++s->j_0;
	
	return true;
}

bool up_mov(p_node &s)	//空格上移
{
	int x=s->i_0,y=s->j_0;
	
	if (x==0)
	{
		return false;
	}
	
	int t;
	t=s->a[x][y];
	s->a[x][y]=s->a[x-1][y];
	s->a[x-1][y]=t;
	
	--s->i_0;
	
	return true;
}

bool down_mov(p_node &s)	//空格下移
{
	int x=s->i_0,y=s->j_0;
	
	if (x==2)
	{
		return false;
	}
	
	int t;
	t=s->a[x][y];
	s->a[x][y]=s->a[x+1][y];
	s->a[x+1][y]=t;
	
	++s->i_0;
	
	return true;
}

bool is_equal(p_node s1,p_node s2)	//判断两节点是否相等
{
	for (int i=0;i<3;i++)
	{
		for (int j=0;j<3;j++)
		{
			if (s1->a[i][j]!=s2->a[i][j])
			{
				return false;
			}
		}
	}

	return true;
}

void copy_node(p_node s1,p_node &s2)	//生成新的结点(将s2赋值给s1)
{

	for (int i=0;i<3;i++)
	{
		for (int j=0;j<3;j++)
		{
			s1->a[i][j]=s2->a[i][j];
		}
	}

	s1->i_0=s2->i_0;
	s1->j_0=s2->j_0;
	
	s1->d=s2->d;
	s1->w=s2->w;
	s1->f=s2->f;
	s1->father=s2->father;
	
}

void add_list(p_list &l,p_node s)	//加入结点到OPEN表中或CLOSE表中
{

	l->a[l->length++]=s;

}

void delete_list(p_list &l)		//从OPEN表或CLOSE中删除结点
{
	for (int i=0;i<l->length;i++)
	{
		l->a[i]=l->a[i+1];
		
	}	
	l->length--;
}

bool search_list(p_list &l,p_node s)//对表进行查找,成功返回true
{
	for(int i=0;i<l->length;i++)
	{
		if(is_equal(l->a[i],s))
			return true;
	}

	return false;
}

void move(p_node s)//移动父节点并加入未探索表中(扩展结点)
{

	p_node p1=(p_node) malloc(sizeof(node));
	p_node p2=(p_node) malloc(sizeof(node));
	p_node p3=(p_node) malloc(sizeof(node));
	p_node p4=(p_node) malloc(sizeof(node));
	

	copy_node(p1,s);
	copy_node(p2,s);
	copy_node(p3,s);
	copy_node(p4,s);

	p1->father=s;
	p2->father=s;
	p3->father=s;
	p4->father=s;


	//如果能够移动且在CLOSED表中不存在,则加入OPEN表中

	if(left_mov(p1) && !is_equal(p1,p1->father) && !search_list(CLOSED,p1) && !search_list(OPEN,p1))
	{
		add_list(OPEN,p1);
		
		p1->d+=1;
		p1->w=w(p1);
		p1->f=f(p1);
	}
	else
	{
		free(p1);
	}
	
	if(right_mov(p2) && !is_equal(p2,p2->father) && !search_list(CLOSED,p2) && !search_list(OPEN,p2))
	{
		add_list(OPEN,p2);
		
		p2->d+=1;
		p2->w=w(p2);
		p2->f=f(p2);
	}
	else
	{
		free(p2);
	}

	if(up_mov(p3) && !is_equal(p3,p3->father) && !search_list(CLOSED,p3) && !search_list(OPEN,p3))
	{
		add_list(OPEN,p3);
		
		p3->d+=1;
		p3->w=w(p3);
		p3->f=f(p3);
	}
	else
	{
		free(p3);
	}

	if(down_mov(p4) && !is_equal(p4,p4->father) && !search_list(CLOSED,p4) && !search_list(OPEN,p4))
	{
		add_list(OPEN,p4);
		
		p4->d+=1;
		p4->w=w(p4);
		p4->f=f(p4);
	}
	else
	{
		free(p4);
	}

}

void sort_list(p_list &l)	//对OPEN表进行排序,插入法排序(按f从小到大)
{
	p_node t=(p_node) malloc (sizeof(node));

	for(int i=1;i<l->length;i++)
	{
		if(l->a[i]->f < l->a[i-1]->f)
		{	
			copy_node(t,l->a[i]);
			
			for(int j=i;j>0;j--)
			{
				copy_node(l->a[j],l->a[j-1]);
			}
			copy_node(l->a[j],t);
		}
	}	
}

void list_reverse(p_node &p)		//将链表逆序
{
	p_node p_pre,p_suc;

	p_pre=NULL;
	p_suc=p->father;

	while(p)
	{
		p->father=p_pre;
		p_pre=p;
		if(p_suc == NULL)
		{
			break;
		}
		p=p_suc;
		p_suc=p_suc->father;
	}
}

void init_node()	//初始化
{
	//---------------------------------------初始化初始结点

	for (int i=0;i<3;i++)
	{
		for (int j=0;j<3;j++)
		{
			s_0->a[i][j]=s0[i][j];

			if(s_0->a[i][j] == 0)
			{
				s_0->i_0=i;
				s_0->j_0=j;
			}
		}
	}

	s_0->d=0;
	s_0->father=NULL;
	s_0->w=w(s_0);
	s_0->f=f(s_0);

	//---------------------------------------初始化目标结点

	for (i=0;i<3;i++)
	{
		for (int j=0;j<3;j++)
		{
			s_g->a[i][j]=sg[i][j];

			if(s_0->a[i][j] == 0)
			{
				s_g->i_0=i;
				s_g->j_0=j;
			}
		}
	}

	s_g->d=0;
	s_g->w=w(s_g);
	s_g->f=f(s_g);

	OPEN->length=0;
	CLOSED->length=0;

	add_list(OPEN,s_0);	//初始结点加入OPEN表中

	printf("初始结点为:\n");
	out_node(s_0);

	printf("目标结点为:\n");
	out_node(s_g);
}



运行结果:




上次更改时间:2012-12-31


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值