A算法求解八数码问题(C实现)

A算法求解八数码问题(C实现)

A算法特点在于对估价函数f的定义上。对于一般的启发式图搜索,总是选择估价函数f值最小的节点作为扩展节点。

估价函数:
f(n)= g(n)+ h(n)

g(n)为初始状态到状态n是已付出的实际代价;
h(n)是从状态n到目标状态的最优路径的估计代价,而搜索的启发式信息主要由h(n)决定

流程:

  1. 将起始点加入open表
  2. 当open表不为空时:
  3. 寻找open表中f值最小的点current
  4. 如果current是终止点,则找到结果,程序结束。
  5. 否则,open表移出current,对current表中的每一个临近点:
    若它不可走或在close表中,略过
    若它不在open表中,加入。
    若它在open表中,计算g值,若g值更小,替换其父节点为current,更新它的g值。
  6. 若open表为空,则路径不存在。

这里为了方便遍历节点是否已经在OPEN/CLOSE表中出现过,采用哈希函数来计算每个节点的哈希值存入CLOSE/OPEN数组中以方便后续遍历

这里的估价函数:取一棋局与目标棋局相比,其位置不符的数码数目

#include<stdio.h>
#include<string.h>
#include<math.h>
#define INF 0x3F3F3F3F;

int target[3][3];

int OPEN[100][3][3];
int OPEN_hash[100];
int now_cost[100];      // 当前节点已付出的实际代价,同时用于判断该位置是否空闲
int OPEN_count = 0;

int CLOSE[100];
int CLOSE_count = 0;

int hash(int* p)
{
	int cnt = 0;
	int sum = 0;
	for (int i = 0; i < 9; ++i) {
		sum += p[i] * pow(9, cnt);
		cnt++;
	}
	return sum;
}

int h(int* p)
{
	int cnt = 0;
	for (int i = 0; i < 9; ++i) {
		if(p[i] == 0)
			continue;
		if(p[i] != *((int*)target + i))
			cnt++;
	}
	return cnt;
}

int isend(int* p)
{
	for (int i = 0; i < 9; ++i) {
		if(p[i] != *((int*)target + i))
			return 0;
	}
	return 1;
}

void show_board(int* board)
{
	int cnt = 0;
	for(int i = 0; i < 3; i++) {
		for(int j = 0; j < 3; j++)
			printf("%d ", board[cnt++]);
		printf("\n");
	}
}

int main(int argc, char const* argv[])
{
	for(int i = 0; i < 100; i++)
		now_cost[i] = -1;
	printf("Please input the initial board(if empty, please input 0)\n");
	int begin[3][3];
	for(int i = 0; i < 3; i++) {
		for(int j = 0; j < 3; j++)
			scanf("%d", &begin[i][j]);
	}
	printf("Please input the target board(if empty, please input 0)\n");
	for(int i = 0; i < 3; i++) {
		for(int j = 0; j < 3; j++)
			scanf("%d", &target[i][j]);
	}
	memcpy(OPEN, begin, 9 * 4);
	now_cost[0] = 0;
	OPEN_hash[0] = hash((int*)begin);
	OPEN_count++;
	printf("---------------------------------\n");

	while(OPEN_count) {
		int pos = -1;
		int f = INF;
		for (int i = 0; i < 100; ++i) {
			if(now_cost[i] == -1) continue;
			if(now_cost[i] + h((int*)OPEN + 9 * i) < f) {
				f = now_cost[i] + h((int*)OPEN + 9 * i);
				pos = i;
			}
		}
		printf("Get a point with samllest f in OPEN:\n");
		show_board((int*)OPEN + 9 * pos);
		if(isend((int*)OPEN + 9 * pos)) {
			printf("Find it! The cost is %d.\n", now_cost[pos]);
			break;
		}

		int temp_cost = now_cost[pos];
		int temp_board[3][3];
		memcpy(temp_board, (int*)OPEN + pos * 9, 4 * 9);
		now_cost[pos] = -1;
		OPEN_count--;
		CLOSE[CLOSE_count] = hash((int*)temp_board);
		CLOSE_count++;

		int x = -1, y = -1;
		for (int i = 0; i < 3; ++i) {
			for (int j = 0; j < 3; ++j) {
				if(temp_board[i][j] == 0) {
					x = i;
					y = j;
				}
			}
		}

		int step_x[4]  = {0, 0, 1, -1};  // 右,左,下,上
		int step_y[4]  = {1, -1, 0, 0};

		for(int i = 0; i < 4; i++) {
			// 不可走
			if( x + step_x[i] < 0 || x + step_x[i] > 2 || y + step_y[i] < 0 || y + step_y[i] > 2)
				continue;

			int xtemp = temp_board[x][y];
			temp_board[x][y] = temp_board[x + step_x[i]][y + step_y[i]];
			temp_board[x + step_x[i]][y + step_y[i]] = xtemp;
			printf("For a proximity point:\n");
			show_board((int*)temp_board);

			// 已经在CLOSE表中
			int hash1 = hash((int*)temp_board);
			int has_been_in_CLOSE = 0;
			for(int k = 0; k < CLOSE_count; k++ ) {
				if(hash1 == CLOSE[k]) {
					has_been_in_CLOSE = 1;
					break;
				}
			}
			if(has_been_in_CLOSE) {
				printf("It has been in CLOSE.\n");
				xtemp = temp_board[x][y];
				temp_board[x][y] = temp_board[x + step_x[i]][y + step_y[i]];
				temp_board[x + step_x[i]][y + step_y[i]] = xtemp;
				continue;
			}

			int is_in_OPEN = 0;
			// 在OPEN表中,则替换
			for(int p = 0; p < 100; p++) {
				if(now_cost[p] == -1) continue;
				if(hash1 == OPEN_hash[p]) {
					is_in_OPEN = 1;
					if(now_cost[p] < temp_cost + 1) {
						memcpy(OPEN + p * 9, temp_board, 9 * 4);
						now_cost[p] = temp_cost + 1;    // 这个1是又走了一步带来的代价
						OPEN_hash[p] = hash1;
						// 替换,则OPEN_count不变
						printf("It has been in OPEN and the new one's g smaller than before, replace it.\n");
					}
					break;
				}
			}

			// 不在OPEN表中,则加入
			if(!is_in_OPEN) {
				for(int p = 0; p < 100; p++) {  // 找到空闲位置
					if(now_cost[p] == -1) {
						memcpy((int*)OPEN + p * 9, temp_board, 9 * 4);
						now_cost[p] = temp_cost + 1;
						OPEN_hash[p] = hash1;
						OPEN_count++;
						printf("It has not been in OPEN yet, add it to OPEN.\n");
						break;
					}
				}
			}

			// 最后别忘了变回来
			xtemp = temp_board[x][y];
			temp_board[x][y] = temp_board[x + step_x[i]][y + step_y[i]];
			temp_board[x + step_x[i]][y + step_y[i]] = xtemp;
		}

	}
	return 0;
}
  • 1
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值