關于A*(Astar)算法的介紹可以參考另一篇博文: A*(Astar)搜尋算法的實作(C語言)
1.問題描述
在3×3的棋盤,擺有八個棋子,每個棋子上标有1至8的某一數字,不同棋子上标的數字不相同。棋盤上還有一個空格,與空格相鄰的棋子可以移到空格中。要求解決的問題是:給出一個初始狀态和一個目标狀态,找出一種從初始轉變成目标狀态的移動棋子步數最少的移動步驟。
2.問題可解性
八數位問題的一個狀态實際上是0~9的一個排列,對于任意給定的初始狀态和目标,不一定有解,也就是說從初始狀态不一定能到達目标狀态。因為排列有奇排列和偶排列兩類,從奇排列不能轉化成偶排列或相反。 如果一個數字0~8的随機排列871526340,用F(X)表示數字X前面比它小的數的個數,全部數字的F(X)之和為Y=∑(F(X)),如果Y為奇數則稱原數字的排列是奇排列,如果Y為偶數則稱原數字的排列是偶排列。 例如871526340這個排列的 Y=0+0+0+1+1+3+2+3+0=10 10是偶數,是以他偶排列。871625340 Y=0+0+0+1+1+2+2+3+0=9 9是奇數,是以他奇排列。 是以,可以在運作程式前檢查初始狀态和目标狀态的窘是否相同,相同則問題可解,應當能搜尋到路徑。否則無解。
3.求解步驟
1)建立一個隊列,計算初始結點的估價函數f,并将初始結點入隊,設定隊列頭和尾指針。 2)取出隊列頭(隊列頭指針所指)的結點,如果該結點是目标結點,則輸出路徑,程式結束。否則對結點進行擴充。 3)檢查擴充出的新結點是否與隊列中的結點重複,若與不能再擴充的結點重複(位于隊列頭指針之前),則将它抛棄;若新結點與待擴充的結點重複(位于隊列頭指針之後),則比較兩個結點的估價函數中g的大小,保留較小g值的結點。跳至第五步。 4)如果擴充出的新結點與隊列中的結點不重複,則按照它的估價函數f大小将它插入隊列中的頭結點後待擴充結點的适當位置,使它們按從小到大的順序排列,最後更新隊列尾指針。 5)如果隊列頭的結點還可以擴充,直接傳回第二步。否則将隊列頭指針指向下一結點,再傳回第二步。
4.代碼實作
二叉堆代碼就不貼出來了,在另一篇博文 A*(Astar)搜尋算法的實作(C語言) 中已經給出,在本文最後有資源下載下傳的連結。可以拿到完整的代碼 astar_8.h
/*
* author: Atom
* date: 2012/12/04
* file: astar_8.h
*/
#ifndef ASTAR_8_H
#define ASTAR_8_H
#include "bheap.h"
#define MALLOC(type,n) (type *)malloc((n)*sizeof(type))
#define MAX(a,b) ((a)>(b))?(a):(b)
#define UP -3
#define RIGHT 1
#define DOWN 3
#define LEFT -1
int node_distance[9][9];
struct step_node
{
int step_status[9];
int f;
int g;
int h;
struct step_node* parent;
};
int _comp(struct Bheap_node* n1, struct Bheap_node* n2);
int _eq(struct Bheap_node* n1, struct Bheap_node* n2);
void astar_8(int start[9], int end[9]);
static int calc_distance(int step_status[9], int end[9]);
static int arr_idx(int n, int step_status[9]);
static void init_node_distance(int node_distance[9][9]);
static int is_end(int step_status[9], int end[9]);
static int is_reachable(int space_idx, int flag);
static int move_space(int step_status[9], int flag);
static void print_step_status(int step_status[9]);
static int check_whether_reach(int start[9], int end[9]);
#endif
astar_8.c
/*
* author: Atom
* date: 2012/12/04
* file: astar_8.c
*/
#include "astar_8.h"
int main(int argc, char* argv[])
{
int i,j;
init_node_distance(node_distance);
int start[] = {2,3,4,1,8,5,0,7,6};
int end[] = {1,2,3,8,0,4,7,6,5};
#if 1
if(check_whether_reach(start, end))
{
printf("sorry,No solution!\n");
}
else
astar_8(start, end);
#else
astar_8(start, end);
#endif
return (0);
}
static int check_whether_reach(int start[9], int end[9])
{
int re_st = 0, re_end = 0;
int tmp = 0;
int i, j;
for (i = 0; i < 9; i++)
{
for(j = 0; j < i; j++)
if (start[i] > start[j])
re_st++;
}
for (i = 0; i < 9; i++)
{
for(j = 0; j < i; j++)
if (end[i] > end[j])
re_end++;
}
return ((re_st^re_end)&0x01);
}
void astar_8(int start[9], int end[9])
{
int space_idx;
int* tmp_step = NULL;
struct Bheap *o_heap = NULL, *c_heap = NULL;
struct Bheap_node *inode = NULL, *onode = NULL;
struct step_node *fnode = NULL;
struct step_node *omnode = NULL;
struct Bheap_node *exist_node = NULL;
size_t idx;
o_heap = Bheap_create(128, BHEAP_TYPE_SMALL);
c_heap = Bheap_create(128, BHEAP_TYPE_SMALL);
/* 第一步判斷可否由start狀态到end狀态 */
/* 将起始點入open heap */
if (NULL == (fnode = MALLOC(struct step_node, 1)))
{
fprintf(stderr, "malloc fnode error!\n");
return;
}
if (NULL == (inode = MALLOC(struct Bheap_node, 1)))
{
fprintf(stderr, "malloc inode error!\n");
return;
}
if (NULL == (tmp_step = MALLOC(int, 9)))
{
fprintf(stderr, "malloc tmp_step error!\n");
return;
}
memset(fnode, 0x00, sizeof(struct step_node));
memset(inode, 0x00, sizeof(struct Bheap_node));
memset(tmp_step, 0x00, sizeof(int) * 9);
memcpy(fnode->step_status, start, sizeof(int) * 9);
fnode->g = 0;
fnode->h = calc_distance(fnode->step_status, end);
fnode->f = fnode->g + fnode->h;
fnode->parent = NULL;
inode->value = fnode;
Bheap_push(o_heap, inode, _comp);
for ( ; ; )
{
omnode = NULL;
if (NULL == (onode = Bheap_pop(o_heap, _comp)))
{
break;
}
else
{
omnode = (struct step_node*)onode->value;
if (is_end(omnode->step_status, end))
break;
Bheap_push(c_heap, onode, _comp);
memset(tmp_step, 0x00, sizeof(int) * 9);
memcpy(tmp_step, omnode->step_status, sizeof(int) * 9);
space_idx = arr_idx(0, tmp_step);
/* 上 */
if (is_reachable(space_idx, (UP)))
{
fnode = NULL;
inode = NULL;
if (NULL == (fnode = MALLOC(struct step_node, 1)))
{
fprintf(stderr, "malloc fnode error!\n");
return;
}
if (NULL == (inode = MALLOC(struct Bheap_node, 1)))
{
fprintf(stderr, "malloc inode error!\n");
return;
}
move_space(tmp_step, (UP));
memcpy(fnode->step_status, tmp_step, sizeof(int) * 9);
fnode->g = omnode->g + 1;
fnode->h = calc_distance(tmp_step, end);
fnode->f = fnode->g + fnode->h;
fnode->parent = omnode;
inode->value = fnode;
/* 不在open heap和closed heap 中 */
if (-1 == is_Bheap_contain(o_heap, inode, _eq)
&& -1 == is_Bheap_contain(c_heap, inode, _eq))
{
Bheap_push(o_heap, inode, _comp);
if (is_end(tmp_step, end))
continue;
}
else if(-1 != (idx = is_Bheap_contain(o_heap, inode, _eq)))
{
/* 在open heap 中*/
if (NULL != (exist_node = Bheap_get(o_heap, idx)))
{
if (fnode->f < ((struct step_node *)exist_node->value)->f)
{
((struct step_node *)(exist_node->value))->f = fnode->f;
((struct step_node*)(exist_node->value))->parent = fnode->parent;
}
}
free(fnode);
free(inode);
}
else
{
free(fnode);
free(inode);
}
Bheap_push(c_heap, onode, _comp);
}
memset(tmp_step, 0x00, sizeof(int) * 9);
memcpy(tmp_step, omnode->step_status, sizeof(int) * 9);
space_idx = arr_idx(0, tmp_step);
/* 下 */
if (is_reachable(space_idx, (DOWN)))
{
fnode = NULL;
inode = NULL;
if (NULL == (fnode = MALLOC(struct step_node, 1)))
{
fprintf(stderr, "malloc fnode error!\n");
return;
}
if (NULL == (inode = MALLOC(struct Bheap_node, 1)))
{
fprintf(stderr, "malloc inode error!\n");
return;
}
move_space(tmp_step, (DOWN));
memcpy(fnode->step_status, tmp_step, sizeof(int) * 9);
fnode->g = omnode->g + 1;
fnode->h = calc_distance(tmp_step, end);
fnode->f = fnode->g + fnode->h;
fnode->parent = omnode;
inode->value = fnode;
/* 不在open heap和closed heap 中 */
if (-1 == is_Bheap_contain(o_heap, inode, _eq)
&& -1 == is_Bheap_contain(c_heap, inode, _eq))
{
Bheap_push(o_heap, inode, _comp);
if (is_end(tmp_step, end))
continue;
}
else if(-1 != (idx = is_Bheap_contain(o_heap, inode, _eq)))
{
/* 在open heap 中*/
if (NULL != (exist_node = Bheap_get(o_heap, idx)))
{
if (fnode->f < ((struct step_node *)exist_node->value)->f)
{
((struct step_node *)(exist_node->value))->f = fnode->f;
((struct step_node*)(exist_node->value))->parent = fnode->parent;
}
}
free(fnode);
free(inode);
}
else
{
free(fnode);
free(inode);
}
Bheap_push(c_heap, onode, _comp);
}
memset(tmp_step, 0x00, sizeof(int) * 9);
memcpy(tmp_step, omnode->step_status, sizeof(int) * 9);
space_idx = arr_idx(0, tmp_step);
/* 左 */
if (is_reachable(space_idx, (LEFT)))
{
fnode = NULL;
inode = NULL;
if (NULL == (fnode = MALLOC(struct step_node, 1)))
{
fprintf(stderr, "malloc fnode error!\n");
return;
}
if (NULL == (inode = MALLOC(struct Bheap_node, 1)))
{
fprintf(stderr, "malloc inode error!\n");
return;
}
move_space(tmp_step, (LEFT));
memcpy(fnode->step_status, tmp_step, sizeof(int) * 9);
fnode->g = omnode->g + 1;
fnode->h = calc_distance(tmp_step, end);
fnode->f = fnode->g + fnode->h;
fnode->parent = omnode;
inode->value = fnode;
/* 不在open heap和closed heap 中 */
if (-1 == is_Bheap_contain(o_heap, inode, _eq)
&& -1 == is_Bheap_contain(c_heap, inode, _eq))
{
Bheap_push(o_heap, inode, _comp);
if (is_end(tmp_step, end))
continue;
}
else if(-1 != (idx = is_Bheap_contain(o_heap, inode, _eq)))
{
/* 在open heap 中*/
if (NULL != (exist_node = Bheap_get(o_heap, idx)))
{
if (fnode->f < ((struct step_node *)exist_node->value)->f)
{
((struct step_node *)(exist_node->value))->f = fnode->f;
((struct step_node*)(exist_node->value))->parent = fnode->parent;
}
}
free(fnode);
free(inode);
}
else
{
free(fnode);
free(inode);
}
Bheap_push(c_heap, onode, _comp);
}
memset(tmp_step, 0x00, sizeof(int) * 9);
memcpy(tmp_step, omnode->step_status, sizeof(int) * 9);
space_idx = arr_idx(0, tmp_step);
/* 右 */
if (is_reachable(space_idx, (RIGHT)))
{
fnode = NULL;
inode = NULL;
if (NULL == (fnode = MALLOC(struct step_node, 1)))
{
fprintf(stderr, "malloc fnode error!\n");
return;
}
if (NULL == (inode = MALLOC(struct Bheap_node, 1)))
{
fprintf(stderr, "malloc inode error!\n");
return;
}
move_space(tmp_step, (RIGHT));
memcpy(fnode->step_status, tmp_step, sizeof(int) * 9);
fnode->g = omnode->g + 1;
fnode->h = calc_distance(tmp_step, end);
fnode->f = fnode->g + fnode->h;
fnode->parent = omnode;
inode->value = fnode;
/* 不在open heap和closed heap 中 */
if (-1 == is_Bheap_contain(o_heap, inode, _eq)
&& -1 == is_Bheap_contain(c_heap, inode, _eq))
{
Bheap_push(o_heap, inode, _comp);
if (is_end(tmp_step, end))
continue;
}
else if(-1 != (idx = is_Bheap_contain(o_heap, inode, _eq)))
{
/* 在open heap 中*/
if (NULL != (exist_node = Bheap_get(o_heap, idx)))
{
if (fnode->f < ((struct step_node *)exist_node->value)->f)
{
((struct step_node *)(exist_node->value))->f = fnode->f;
((struct step_node*)(exist_node->value))->parent = fnode->parent;
}
}
free(fnode);
free(inode);
}
else
{
free(fnode);
free(inode);
}
Bheap_push(c_heap, onode, _comp);
}
}
}
if (NULL == omnode)
{
printf("not found!\n");
}
else
{
while(NULL != omnode)
{
print_step_status(omnode->step_status);
printf("\n");
omnode = omnode->parent;
}
}
}
static int arr_idx(int n, int step_status[9])
{
int cnt;
for(cnt = 0; cnt < 9; cnt++)
if (n == step_status[cnt])
return cnt;
return (-1);
}
static void init_node_distance(int node_distance[9][9])
{
int i, j, val;
for (i = 0; i < 9; i++)
{
for (j = 0; j < 9; j++)
{
val = abs(i - j);
node_distance[i][j] = (int)val/3 + val%3;
}
}
}
static int calc_distance(int step_status[9], int end[9])
{
int i, tmp, ret_val = 0;
for (i = 0; i < 9; i++)
if (-1 != (tmp = arr_idx(step_status[i], end)))
ret_val += node_distance[i][tmp];
return (ret_val);
}
static int is_end(int step_status[9], int end[9])
{
int i;
for (i = 0; i < 9; i++)
if (step_status[i] != end[i])
return (0);
return (1);
}
static int is_reachable(int space_idx, int flag)
{
int x,y;
x = space_idx/3;
y = space_idx%3;
if(((UP) == flag) && (x > 0))
return (1);
else if (((DOWN) == flag) && (x < 2))
return (1);
else if (((LEFT) == flag) && (y > 0))
return (1);
else if(((RIGHT) == flag) && (y < 2))
return (1);
else
return (0);
}
static int move_space(int step_status[9], int flag)
{
int zero_idx, tmp;
zero_idx = arr_idx(0, step_status);
tmp = step_status[zero_idx];
/* 交換空格的位置 */
if (((UP) == flag) || ((DOWN) == flag) || ((LEFT) == flag) || ((RIGHT) == flag))
{
step_status[zero_idx] = step_status[zero_idx + flag];
step_status[zero_idx + flag] = tmp;
return (0);
}
return (-1);
}
static void print_step_status(int step_status[9])
{
int i;
for(i = 0; i < 9; i++)
{
printf("%d ", step_status[i]);
if (0 == ((i+1)%3) && (i != 0))
printf("\n");
}
}
/* Bheap_compare_t 函數實作 */
int _comp(struct Bheap_node* n1, struct Bheap_node* n2)
{
struct step_node *sn1 = NULL, *sn2 = NULL;
if ((NULL != n1) && (NULL != n2))
{
sn1 = (struct step_node*)n1->value;
sn2 = (struct step_node*)n2->value;
if (sn1->f > sn2->f)
return (1);
else if(sn1->f == sn2->f)
return (0);
else
return (-1);
}
else
return (0);
}
/* Bheap_equal_t 函數實作 */
int _eq(struct Bheap_node* n1, struct Bheap_node* n2)
{
struct step_node *sn1 = NULL, *sn2 = NULL;
if ((NULL != n1) && (NULL != n2))
{
sn1 = (struct step_node*)(n1->value);
sn2 = (struct step_node*)(n2->value);
return calc_distance(sn1->step_status, sn2->step_status);
}
else
return (0);
}
執行結果:
代碼包下載下傳: 八數位問題實作(C語言) Github位址: https://github.com/xielianghai/astar_8