人工智能A*算法解决八数码问题代码学习总结

前言:

A*算法的原理网上有很多,文章都讲的非常详细,我就不再多说了。

简单分享一下学习的体验:

思路必须清晰,不然会写的很痛苦。我写这个代码花费了很长时间,大部分时间花在了构思和调试上。

先说构思。我在看过A*算法解决八数码问题的原理之后,第一时间想:

结构体实现八数码,优先队列实现OPEN,CLOSE表,整体流程就是初始化,取OEPN表结点,移动派生子节点,该结点添加到CLOSE表,循环往复,直至移动到正确位置。

但是问题出在哪了呢?

八数码生成树的整体结构我没弄清楚。所以一开始我写的结构体他就不适合这个树,不适合去构造最优解。虽然整体流程我能说个差不多的意思,但落实到代码里,各种细节,特别是条件语句,这个条件我是没弄明白的,最后我写出来的代码求不出来答案。

本来打算复制粘贴了,去网上翻了翻代码,但是自己心有不甘,明明付出了这么多都要白费了吗,又开始写了起来。网上大牛写的代码确实好,看了之后自己的脑子清晰了很多。

后来我做的最多的工作就是拆分和细化,我知道具体这一部分是做什么用的,组合起来有实现什么功能,整个思路就丰富了,代码里该有的部分都补充和优化好了。

然后说说调试出现的问题:

首先是指针,这个每次都要调试。然后就是各种操作函数的调试,写代码最怕的就是看不见的那一部分,和你预期的不一样很难发现错误,先保证各种函数是正确的再调用。

这一次出的最大错误是在优先队列自定义排序,我用的是结构体指针,正确的做法是写一个自己的compare,可以用结构体写,里面实现对结构体的排序。但起初我不知道,我用了重载运算符,重载运算符结构体可以用,但是指针类型不行,我花了很长时间才找到这个错误。

最后也是顺利的写出来了,简单样例可以跑出来结果,现在觉得也不是那么复杂。

不过自己随便输的样例还是求不出来,算了,我已经停止思考了......

测试代码:

#include <iostream>
#include <queue>
#include <vector>
using namespace std;

/*-----------------------------------------目标八数码*/
int recell[3][3] = {
	1, 2, 3,
	8, 0, 4,
	7, 6, 5
};

int res_pos_x(int num) {			//返回数字的索引
	int x = 0;
	switch (num) {
	case 1: x = 0; break;
	case 2: x = 0; break;
	case 3: x = 0; break;
	case 4: x = 1; break;
	case 5: x = 2; break;
	case 6: x = 2; break;
	case 7: x = 2; break;
	case 8: x = 1; break;
	case 0: x = 1; break;
	}
	return x;
}

int res_pos_y(int num) {
	int y = 0;
	switch (num) {
	case 1: y = 0; break;
	case 2: y = 1; break;
	case 3: y = 2; break;
	case 4: y = 2; break;
	case 5: y = 2; break;
	case 6: y = 1; break;
	case 7: y = 0; break;
	case 8: y = 0; break;
	case 0: y = 1; break;
	}
	return y;
}
/*---------------------------------------------------*/



/*-------------------------------------------数据结构*/
#define CELL_SIZE 3					//八数码尺寸
#define DIRECT_SIZE 4				//可移动方向
int MAX_ITER_SIZE;					//最大计算次数
#define UP 0		
#define RIGHT 1
#define DOWN 2
#define LEFT 3
int measure;						//计算方法标志
typedef struct node {				//八数码结点结构体
	int pos_0[2];					//0的位置
	int cell[CELL_SIZE][CELL_SIZE];	//八数码数组
	int f, g, h;					//启发式函数变量
	struct node* pre;				//父节点指针
	int getf(int level) {			//启发式函数 计算并返回f(x)
		g = level;					//深度作为g(x)
		h = 0;
		for (int i = 0; i < CELL_SIZE; i++) {
			for (int j = 0, num; j < CELL_SIZE; j++) {
				num = cell[i][j];
				switch (measure) {	//计算过程中忽略0的影响
				case 1:				//不在位个数
					h += (num == 0) ? 0 : ((i == res_pos_x(num) && j == res_pos_y(num)) ? 0 : 1);
					break;			
				case 2:				//曼哈顿距离
					h += (num == 0) ? 0 : (abs(i - res_pos_x(num)) + abs(j - res_pos_y(num)));
					break;
				case 3:				//直线距离
					h += (num == 0) ? 0 : (int)sqrt((i - res_pos_x(num) * (i - res_pos_x(num)) + (j - res_pos_y(num)) * (j - res_pos_y(num))));
					break;
				default:			//默认 不在位个数
					h += (num == 0) ? 0 : ((i == res_pos_x(num) && j == res_pos_y(num)) ? 0 : 1);
					break;
				}
			}
		}
		f = g + h;
		return f;
	}
	void getCell() {				//打印结点基本数据
		for (int i = 0; i < CELL_SIZE; i++) {
			for (int j = 0; j < CELL_SIZE; j++) {
				cout << cell[i][j] << " ";
			}
			cout << endl;
		}
		cout << "f:" << f << " g:" << g << " h:" << h << endl;
	}
}*eight_puzzle;

//优先队列实现OPEN表 重写比较函数实现优先级
struct cmp{
	bool operator() (const eight_puzzle a, const eight_puzzle b){
		return a->f > b->f;
	}
};
priority_queue<eight_puzzle, vector<eight_puzzle>, cmp> open;
vector<eight_puzzle> close;	
int lo,		//OPEN表长度
	lc;		//CLOSE表长度

//定义起始结点和目标结点
eight_puzzle start = (eight_puzzle)malloc(sizeof(struct node));
eight_puzzle target = (eight_puzzle)malloc(sizeof(struct node));
/*---------------------------------------------------*/



/*-----------------------------------------------函数*/
void swap(int* a, int* b);							//交换数字 a 和 b												
void copy(eight_puzzle a, eight_puzzle b);			//复制结点 b 到 a
bool isequal(eight_puzzle a, eight_puzzle b);		//比较结点 a 和 b
void concat(int a[CELL_SIZE][CELL_SIZE],			//复制结点 a 的数组 到 b
			const int b[CELL_SIZE][CELL_SIZE]);		
void move(eight_puzzle a);							//以当前结点 a 为扩展结点进行移动
bool isclosed(eight_puzzle a);						//结点是否在CLOSE表中
void init();										//初始化 起始结点 和 目标结点
void backtravel(eight_puzzle a);					//回溯遍历 输出最优解

void swap(int* a, int* b) {
	int *t = a;
	a = b;
	b = t;
}

void copy(eight_puzzle a, const eight_puzzle b) {
	a->f = b->f;
	a->g = b->g;
	a->h = b->h;
	a->pos_0[0] = b->pos_0[0];
	a->pos_0[1] = b->pos_0[1];
	concat(a->cell, b->cell);
}

bool isequal(eight_puzzle a, eight_puzzle b) {
	for (int i = 0; i < CELL_SIZE; i++) {
		for (int j = 0; j < CELL_SIZE; j++) {
			if (a->cell[i][j] != b->cell[i][j]) return false;
		}
	}
	return true;
}

void concat(int a[CELL_SIZE][CELL_SIZE], const int b[CELL_SIZE][CELL_SIZE]) {
	for (int i = 0; i < CELL_SIZE; i++) {
		for (int j = 0; j < CELL_SIZE; j++) {
			a[i][j] = b[i][j];
		}
	}
}

void move(eight_puzzle a) {
	/*
		循环 四个方向{
			临时结点 t 
			复制结点 a 到 t
			如果 0 能移动{
				临时结点 t 移动 
				临时结点 t 父节点指向 a
				添加 临时结点 t 到 OPEN表
			}
		}
	*/
	for(int i, j, d = 0; d < DIRECT_SIZE; d++){
		eight_puzzle t = (eight_puzzle)malloc(sizeof(struct node));
		copy(t, a);
		i = t->pos_0[0];
		j = t->pos_0[1];
		switch (d) {
			case UP:	
				if (i - 1 > -1) {
					swap(t->cell[i][j], t->cell[i - 1][j]);
					t->pos_0[0] = i - 1;
				}
				break;
			case RIGHT: 
				if (j + 1 < CELL_SIZE) {
					swap(t->cell[i][j], t->cell[i][j + 1]);
					t->pos_0[1] = j + 1;
				}
				break;
			case DOWN:	
				if (i + 1 < CELL_SIZE) {
					swap(t->cell[i][j], t->cell[i + 1][j]);
					t->pos_0[0] = i + 1;
				}
				break;
			case LEFT:	
				if (j - 1 > -1) {
					swap(t->cell[i][j], t->cell[i][j - 1]);
					t->pos_0[1] = j - 1;
				}
				break;
		}
		t->pre = a;
		t->getf(a->g + 1);
		open.push(t); lo++;
	}
}

bool isclosed(eight_puzzle a) {
	for (int i = 0; i < lc; i++) {
		if (isequal(a, close.at(i))) return true;
	}
	return false;
}

void init() {
	start->f = start->g = start->h = 0;
	start->pre = NULL;
	for (int i = 0; i < CELL_SIZE; i++) {
		for (int j = 0; j < CELL_SIZE; j++) {
			cin >> start->cell[i][j];
			if (start->cell[i][j] == 0) {
				start->pos_0[0] = i;
				start->pos_0[1] = j;
			}
		}
	}
	target->f = target->g = target->h = 0;
	target->pre = NULL;
	concat(target->cell, recell);
	lo = lc = 0;
	start->getf(0);
	return;
}

void backtravel(eight_puzzle a) {
	if (a) {
		backtravel(a->pre);
		cout << endl <<  "第 " << a->g << " 步" << endl;
		cout << "------" << endl;
		a->getCell();
		cout << "------" << endl << endl;
	}
}
/*---------------------------------------------------*/

int main() {
	int output, cnt = 0;
	cout << "输入1开启(0关闭) 当前结点八数码 显示" << endl;
	cin >> output;
	cout << endl;
	cout << "请输入最大计算次数" << endl;
	cin >> MAX_ITER_SIZE;
	cout << endl;
	cout << "请选择启发式函数h(x)的计算方法" << endl;
	cout << "输入 1 计算不在位个数" << endl;
	cout << "输入 2 计算曼哈顿距离" << endl;
	cout << "输入 3 计算直线距离" << endl;
	cin >> measure;
	cout << endl;
	cout << "请输入起始八数码" << endl;
	init();
	open.push(start); lo++;
	eight_puzzle cur = (eight_puzzle)malloc(sizeof(struct node));
	while (lo > 0 && lc < MAX_ITER_SIZE) {
		cnt++;
		cur = open.top(); open.pop(); lo--;
		if (output) {
			cout << endl << "------" << endl;
			cur->getCell();
			cout << "------" << endl << endl;
		}
		if (isequal(cur, target)) {
			cout << "寻找到答案!!!" << endl; 
			backtravel(cur);
			cout << "本次计算中0移动了" << cnt - 1 << "次" << endl;
			system("pause");
			return 0;
		}
		close.push_back(cur); lc++;
		move(cur);
	}
	cout << "在一定限度内未能寻找到答案" << endl;
	system("pause");
	return 0;
}

测试样例:2 8 3 1 0 4 7 6 5

测试结果:

八数码生成树:

  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值