A算法求解八数码问题(C实现)
A算法特点在于对估价函数f的定义上。对于一般的启发式图搜索,总是选择估价函数f值最小的节点作为扩展节点。
估价函数:
f(n)= g(n)+ h(n)
g(n)为初始状态到状态n是已付出的实际代价;
h(n)是从状态n到目标状态的最优路径的估计代价,而搜索的启发式信息主要由h(n)决定
流程:
- 将起始点加入open表
- 当open表不为空时:
- 寻找open表中f值最小的点current
- 如果current是终止点,则找到结果,程序结束。
- 否则,open表移出current,对current表中的每一个临近点:
若它不可走或在close表中,略过
若它不在open表中,加入。
若它在open表中,计算g值,若g值更小,替换其父节点为current,更新它的g值。 - 若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;
}