A和A*算法C实现【人工智能】8数码问题和15数码问题

实验目的和要求:

   (1)熟悉掌握启发式搜索算法A*及其可采纳性  

   (2)编写程序实现8数码和15数码问题,采用至少两种估价函数

   (3)分析估价函数求解问题时候的效率差别,分析估价函数对搜索算法的影响

实验思路:

   (1)、用结构体变量存储每个结点的状态值(错位棋牌或者移动步骤)

             定义两个队列CLOSE表和OPEN表用来记录和操作结点

   (2)、初始化:建立只包含初始状态节点s的搜索图G:={s}

              将s入OPEN表,CLOSE表为空

   (3)、搜索循环:搜索结束条件为找到目标结点为止(可以定义一个target变量来记录状态)

               首先取出OPEN表表首结点(队列出队PUSH)

               将此节点入CLOSE表

               按规定顺序(左、下、右、上)扩展结点并且入OPEN表  

               然后分别计算其f(n,ni)作为结点的状态值

               利用归并排序排序OPEN表(依据状态值)

               然后继续循环

实验代码:

#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
#define N 3
//#define N 4
//记录层数的全局变量
int layer = 0;

//保存初始表盘的初态和末态
static int Pstart[N][N] = { 1,0,3,7,2,4,6,8,5 };
static int Pend[N][N] = { 1,2,3,8,0,4,7,6,5 };
//static int Pstart[N][N] = { 1,3,0,4,5,2,7,8,9,6,11,12,13,10,14,15 };
//static int Pend[N][N] = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0 };

//定义每个结点数据类型
typedef struct PNode {
	int status;       //与末态表盘之间的错位个数
	int Ppoint[N][N];  //记录每个小格的数据
}PNode;

//定义open表和close表的结构(采用队列形式)
//队列的定义
typedef struct ListNode {  //定义链表结点
	PNode* pNode;      //数据项
	struct ListNode* List_next;  //指向下一结点的指针域
}ListNode, *pListNode;
typedef struct Queue {    //定义队列相当于两个指针分别控制链表的头尾
	pListNode qHead;	  //指向队列的头	
	pListNode qTail;      //指向队列的尾
}Queue;
Queue* Open_List;      //open表
Queue* Close_List;     //close表
//ListNode* Lhead;
//队列的操作
//创建表盘结点
PNode* CreatePNode() {
	PNode* p = (PNode*)malloc(sizeof(PNode));
	p->status = 0;
	return p;
}
//创建链表结点
ListNode* CreatNode(PNode* p) { //PNode* p p是指向数据结构体的指针
	ListNode* newNode = (ListNode*)malloc(sizeof(ListNode));
	if (!newNode) {
		printf("分配内存空间失败");
		system("pause");
		exit(1);
	}
	newNode->List_next = NULL;
	newNode->pNode = p;
	return newNode;
}
//队列初始化,相当于创建首元结点
void QueueInit(Queue* q) {      //Queue* q是指向队列的指针
	assert(q);
	q->qHead=CreatNode(NULL);
	q->qTail = q->qHead;
}
//入队
void QueuePush(Queue* q,PNode* p) {
	assert(q);
	q->qTail->List_next=CreatNode(p);
	q->qTail = q->qTail->List_next;
}
//出队,将首元结点后的一个结点指针出队并返回
ListNode* QueuePop(Queue* q) {
	pListNode dNode = q->qHead->List_next;        //首元结点后的第一个结点
	if (dNode) {            //存在一个结点
		q->qHead->List_next = dNode->List_next;
		if (q->qHead->List_next == NULL) {        //队列只有一个结点
			q->qTail = q->qHead;
		}
	}
	return dNode;
}
//判断队列是否为空
int QueueEmpty(Queue* q) {
	return NULL == q->qHead->List_next;
}
//计算表的结点个数
int ListLength(ListNode* l) {
	int count=0;   //记录个数
	//首元结点不算一个结点
	l = l->List_next;
	while (l!=NULL) {
		count++;
		l = l->List_next;
	}
	return count;
}

//定义评价函数(错位棋牌个数)
void judgefun1(PNode* p) {
	p->status = 0;
	for (int m = 0;m < N;m++) {
		for (int n = 0;n < N;n++) {
			if (p->Ppoint[m][n] != Pend[m][n]) {
				if (p->Ppoint[m][n] != 0) {
					p->status++;
				}
			}
		}
	}
	p->status = p->status + layer;
}
//定义评价函数(错位棋牌数到达目标位置最少步数)     *****************************????????????
void judgefun2(PNode* p) {
	p->status = 0;
	int i = 0;
	int j = 0;
		do {
			if (p->Ppoint[i][j] == 0) {
				if (j < N - 1) {
					j++;
				}
				else {
					j = 0;
					i++;
				}
				continue;
			}
			for (int m = 0;m < N;m++) {
				for (int n = 0;n < N;n++) {
					if (p->Ppoint[i][j] == Pend[m][n]) {
						p->status = p->status + abs(m - i) + abs(n - j);
					}
				}
			}
			if (j < N - 1) {
				j++;
			}
			else {
				j = 0;
				i++;
			}
		} while (i < N);
	p->status = p->status + layer;
}
//判断是否生成目标结点
bool judgeTarget(PNode* p) {
	for (int m = 0;m < N;m++) {
		for (int n = 0;n < N;n++) {
			if(p->Ppoint[m][n]!=Pend[m][n]){
				return 0;
			}
		}
	}
	return 1;
}

//定义空白格子移动顺序同时判断是否生成目标结点
//左下右上
int move(Queue* q,PNode* p) {        //q为要入队的队列(OPEN表),p为待扩展的结点
	//找到0的位置
	int m;
	int n;
	int t;                //交换中间变量
	PNode* ptmp[4];
	layer++;
	//定位0的位置
	for (m = 0;m < N;m++) {
		for (n = 0;n < N;n++) {
			if (p->Ppoint[m][n] == 0) {
				break;
			}
		}
		if (n < N) {
			break;
		}
	}
	//左移
	if (n != 0) {      //只要不是第一列
		//交换点
		ptmp[0] = CreatePNode();
		for (int j = 0;j < N;j++) {
			for (int k = 0;k < N;k++) {
				ptmp[0]->Ppoint[j][k] = p->Ppoint[j][k];
			}
		}
		t = ptmp[0]->Ppoint[m][n];
		ptmp[0]->Ppoint[m][n] = ptmp[0]->Ppoint[m][n - 1];
		ptmp[0]->Ppoint[m][n-1] = t;
		//judgefun1(ptmp[0]);
		judgefun2(ptmp[0]);
		QueuePush(Open_List, ptmp[0]);     //扩展结点入队
		//判断扩展结点是否是目标结点
		if (judgeTarget(ptmp[0])) {
			//将此结点入CLOSE表
			QueuePush(Close_List, ptmp[0]);
			return 0;
		}
	}
	//下
	if (m != N-1) {      //只要不在第N行
		ptmp[1] = CreatePNode();
		for (int j = 0;j < N;j++) {
			for (int k = 0;k < N;k++) {
				ptmp[1]->Ppoint[j][k] = p->Ppoint[j][k];
			}
		}
		t = ptmp[1]->Ppoint[m][n];
		ptmp[1]->Ppoint[m][n] = ptmp[1]->Ppoint[m + 1][n];
		ptmp[1]->Ppoint[m + 1][n] = t;
		//judgefun1(ptmp[1]);
		judgefun2(ptmp[1]);
		QueuePush(Open_List, ptmp[1]);
		if (judgeTarget(ptmp[1])) {
			//将此结点入CLOSE表
			QueuePush(Close_List, ptmp[1]);
			return 0;
		}
	}
	//右
	if (n != N - 1) {        //只要不在第N列
		ptmp[2] = CreatePNode();
		for (int j = 0;j < N;j++) {
			for (int k = 0;k < N;k++) {
				ptmp[2]->Ppoint[j][k] = p->Ppoint[j][k];
			}
		}
		t = ptmp[2]->Ppoint[m][n];
		ptmp[2]->Ppoint[m][n] = ptmp[2]->Ppoint[m][n + 1];
		ptmp[2]->Ppoint[m][n + 1] = t;
		//计算估计函数的值
		//judgefun1(ptmp[2]);
		judgefun2(ptmp[2]);
		QueuePush(Open_List, ptmp[2]);
		if (judgeTarget(ptmp[2])) {
			//将此结点入CLOSE表
			QueuePush(Close_List, ptmp[2]);
			return 0;
		}
	}
	//上
	if (m != 0) {
		ptmp[3] = CreatePNode();
		for (int j = 0;j < N;j++) {
			for (int k = 0;k < N;k++) {
				ptmp[3]->Ppoint[j][k] = p->Ppoint[j][k];
			}
		}
		t = ptmp[3]->Ppoint[m][n];
		ptmp[3]->Ppoint[m][n] = ptmp[3]->Ppoint[m - 1][n];
		ptmp[3]->Ppoint[m - 1][n] = t;
		//judgefun1(ptmp[3]);
		judgefun2(ptmp[3]);
		QueuePush(Open_List, ptmp[3]);
		if (judgeTarget(ptmp[3])) {
			//将此结点入CLOSE表
			QueuePush(Close_List, ptmp[3]);
			return 0;
		}
	}
	return 1;
}

//排序表:分治法实现
ListNode* list_spilt(ListNode* head) {  //Queue* q                     //传入head->next分裂链表并且返回第二个子链表的首指针
	if (NULL == head) {                  //没有元素可以分裂
		return head;
	}
	ListNode* tmp;                      //用于记录前一个表的表尾
	ListNode* slow = head;
	ListNode* fast = head;              //一个快指针一个慢指针用于定位中间结点

	while (fast) {
		fast = fast->List_next;
		if (fast) {
			fast = fast->List_next;
		}                                    //相当于fast移动两次slow移动一次
		if (NULL == fast) {
			break;
		}
		slow = slow->List_next;
	}
	tmp = slow;
	slow = slow->List_next;            //指向第二个子链表的首
	tmp->List_next = NULL;             //断链
	return slow;
}

ListNode* merge(ListNode* head1, ListNode* head2) {               //合并两个链表,并且返回排序完成之后的指针
	if (NULL == head1)
		return head1;
	if (NULL == head2)
		return head2;

	ListNode head;                   //记录排序链表的首元节点
	ListNode* tail = &head;           

	//尾插法实现
	while (head1 && head2) {
		if (head1->pNode->status < head2->pNode->status) {
			tail->List_next = head1;
			head1 = head1->List_next;
		}
		else {
			tail->List_next = head2;
			head2 = head2->List_next;
		}
		tail = tail->List_next;
	}

	if (head1) {
		tail->List_next = head1;
	}
	if (head2) {
		tail->List_next = head2;
	}
	return head.List_next;
}

ListNode* merge_sort(ListNode* head) {                  //传入head->next
	if (head==NULL || head->List_next==NULL) {      //分裂到只包含一个结点
		return head;
	}

	ListNode* head1 = head;
	ListNode* head2 = list_spilt(head);

	//对链表分别进行分裂
	head1 = merge_sort(head1);
	head2 = merge_sort(head2);
	//对链表合并
	return merge(head1, head2);
}



int main() {
	int steps = 1;
	int target=-1;
	//初始化
	/*
	   建立只包含初始状态结点s的搜索图G
	   OPEN={s}
	   CLOSE={}
	*/
	//先将两表(队列)初始化
	Open_List = (Queue *)malloc(sizeof(Queue));
	Close_List = (Queue*)malloc(sizeof(Queue));
	QueueInit(Open_List);
	QueueInit(Close_List);
	//将s插入open表
	PNode* s = (PNode*)malloc(sizeof(PNode));
	for (int j = 0;j < N;j++) {
		for (int k = 0;k < N;k++) {
			s->Ppoint[j][k] = Pstart[j][k];
		}
	}
	//s->Ppoint = Pstart;   //存入数组
	QueuePush(Open_List, s);   //入队

	//搜索循环(循环条件是扩展结点包含目标结点)
	while (target != 0) {      //当target=1时找到目标结点
		//取出OPEN表首结点作为扩展结点
		ListNode* extendNode = QueuePop(Open_List);
		//将其移入CLOSE表中
		QueuePush(Close_List, extendNode->pNode);
		//扩展结点插入OPEN表中
		target = move(Open_List, extendNode->pNode);
		//对每个子节点算其估计函数(在move函数中实现)
		//排序OPEN
		//ListNode* Lhead = Open_List->qHead->List_next;
		Open_List->qHead->List_next = merge_sort(Open_List->qHead->List_next);
	}
	printf("%d数码问题:空格移动位置步骤为:\n",8);
	//找到目标结点之后输出CLOSE队列找到相应的路径
	Close_List->qHead = Close_List->qHead->List_next;
	while (Close_List->qHead != NULL) {
		printf("-----steps%d-----\n", steps++);
		for (int i = 0;i < N;i++) {
			for (int j = 0;j < N;j++) {
				printf("%d  ", Close_List->qHead->pNode->Ppoint[i][j]);
			}
			printf("\n");
		}
		Close_List->qHead = Close_List->qHead->List_next;
		printf("---------------");
		printf("\n");

	}
	system("pause");
	return 0;
}





测试数据:(两组)

(1)对于8数码问题:1,0,3,7,2,4,6,8,5

         对于15数码问题:1,3,0,4,5,2,7,8,9,6,11,12,13,10,14,15

(2)对于8数码问题:1,2,3,8,0,4,7,6,5

         对于15数码问题:1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0

实验结果:

(1)8数码问题:

 (2)15数码问题:

 

  • 6
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

粒粒米z

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值