双向广度优先搜索法
8数码问题比较简单,一般不超过30步即可求解。搜索的节点也不会超过1000000。而如下这个问题的步数是56,如果直接用广度优先搜索,节点数量会多的难以想象。
#######
#BLACK#
## # ## #是墙
#WHITE#
#######
|
#######
#WHITE#
## # ##
#BLACK#
#######
问题就是将上述两个状态互换。
双向广度优先搜索法我是在“人工智能爱好者”网站上看到的。其思想是同时从开始状态和结束状态开始广度优先搜索,随着搜索的进行,双方会在中间相遇。由此就找到了一个可行解,而且是最少步数解。具体方法是每增加一个节点,就在对方已搜索的节点中查找,如果找到相同状态节点,则为找到解。
以下这个程序和一般的广度优先搜索并无太大区别,区别主要是双向相遇的判断。
/*
-----------
#######
#BLACK#
## # ## #是墙
#WHITE#
#######
|
#######
#WHITE#
## # ##
#BLACK#
#######
-----------
*/
#include <stdio.h>
typedef unsigned long long UINT64;
typedef struct
{
char x; //位置x和位置y上的数字换位
char y; //其中x是0所在的位置
} PZ_MOVE;
typedef struct PZ_NODE_Tag
{
UINT64 v;
struct PZ_NODE_Tag *prev, *small, *big;
} PZ_NODE;
#define ROW 3
#define COL 5
#define NUM (ROW * COL)
#define MAX_NODE 2000000
#define MAX_DEP 100
#define MAX_MOVE 6
#define XCHG(a, b) { a=a + b; b=a - b; a=a - b; }
#define TRANS(a, b) { long iii; (b)=0; for(iii=0; iii < NUM; iii++) (b)=((b) << 4) + a[iii]; } //将数组a转换为一个64位的整数b
#define RTRANS(a, b) /
{ /
long iii; /
UINT64 ttt=(a); /
for(iii=NUM - 1; iii >= 0; iii--) /
{ /
b[iii]=ttt & 0xf; /
ttt>>=4; /
} /
} //将一个64位整数a转换为数组b
//
PZ_NODE m_ar1[MAX_NODE];
long m_depth1; //搜索深度
PZ_NODE m_out1[MAX_DEP]; //输出路径
PZ_NODE *m_root1;
PZ_NODE m_ar2[MAX_NODE];
long m_depth2; //搜索深度
PZ_NODE m_out2[MAX_DEP]; //输出路径
PZ_NODE *m_root2;
//
long move_gen(PZ_NODE *node, PZ_MOVE *mv)
{
long c=0;
char st[NUM];
RTRANS(node->v, st);
if((st[0] != 0) && (st[1] == 0))
{
mv[c].x=1;
mv[c].y=0;
c++;
if(st[2] == 0)
{
mv[c].x=2;
mv[c].y=0;
c++;
}
if(st[6] == 0)
{
mv[c].x=6;
mv[c].y=0;
c++;
}
}
if(st[1])
{
if(st[0] == 0)
{
mv[c].x=0;
mv[c].y=1;
c++;
}
if(st[2] == 0)
{
mv[c].x=2;
mv[c].y=1;
c++;
if(st[3] == 0)
{
mv[c].x=3;
mv[c].y=1;
c++;
}
}
if(st[6] == 0)
{
mv[c].x=6;
mv[c].y=1;
c++;
if(st[11] == 0)
{
mv[c].x=11;
mv[c].y=1;
c++;
}
}
}
if(st[2])
{
if(st[1] == 0)
{
mv[c].x=1;
mv[c].y=2;
c++;
if(st[0] == 0)
{
mv[c].x=0;
mv[c].y=2;
c++;
}
if(st[6] == 0)
{
mv[c].x=6;
mv[c].y=2;
c++;
}
}
if(st[3] == 0)
{
mv[c].x=3;
mv[c].y=2;
c++;
if(st[4] == 0)
{
mv[c].x=4;
mv[c].y=2;
c++;
}
if(st[8] == 0)
{
mv[c].x=8;
mv[c].y=2;
c++;
}
}
}
if(st[3])
{
if(st[4] == 0)
{
mv[c].x=4;
mv[c].y=3;
c++;
}
if(st[2] == 0)
{
mv[c].x=2;
mv[c].y=3;
c++;
if(st[1] == 0)
{
mv[c].x=1;
mv[c].y=3;
c++;
}
}
if(st[8] == 0)
{
mv[c].x=8;
mv[c].y=3;
c++;
if(st[13] == 0)
{
mv[c].x=13;
mv[c].y=3;
c++;
}
}
}
if((st[4] != 0) && (st[3] == 0))
{
mv[c].x=3;
mv[c].y=4;
c++;
if(st[2] == 0)
{
mv[c].x=2;
mv[c].y=4;
c++;
}
if(st[8] == 0)
{
mv[c].x=8;
mv[c].y=4;
c++;
}
}
if((st[10] != 0) && (st[11] == 0))
{
mv[c].x=11;
mv[c].y=10;
c++;
if(st[12] == 0)
{
mv[c].x=12;
mv[c].y=10;
c++;
}
if(st[6] == 0)
{
mv[c].x=6;
mv[c].y=10;
c++;
}
}
if(st[11])
{
if(st[10] == 0)
{
mv[c].x=10;
mv[c].y=11;
c++;
}
if(st[12] == 0)
{
mv[c].x=12;
mv[c].y=11;
c++;
if(st[13] == 0)
{
mv[c].x=13;
mv[c].y=11;
c++;
}
}
if(st[6] == 0)
{
mv[c].x=6;
mv[c].y=11;
c++;
if(st[1] == 0)
{
mv[c].x=1;
mv[c].y=11;
c++;
}
}
}
if(st[12])
{
if(st[11] == 0)
{
mv[c].x=11;
mv[c].y=12;
c++;
if(st[10] == 0)
{
mv[c].x=10;
mv[c].y=12;
c++;
}
if(st[6] == 0)
{
mv[c].x=6;
mv[c].y=12;
c++;
}
}
if(st[13] == 0)
{
mv[c].x=13;
mv[c].y=12;
c++;
if(st[14] == 0)
{
mv[c].x=14;
mv[c].y=12;
c++;
}
if(st[8] == 0)
{
mv[c].x=8;
mv[c].y=12;
c++;
}
}
}
if(st[13])
{
if(st[14] == 0)
{
mv[c].x=14;
mv[c].y=13;
c++;
}
if(st[12] == 0)
{
mv[c].x=12;
mv[c].y=13;
c++;
if(st[11] == 0)
{
mv[c].x=11;
mv[c].y=13;
c++;
}
}
if(st[8] == 0)
{
mv[c].x=8;
mv[c].y=13;
c++;
if(st[3] == 0)
{
mv[c].x=3;
mv[c].y=13;
c++;
}
}
}
if((st[14] != 0) && (st[13] == 0))
{
mv[c].x=13;
mv[c].y=14;
c++;
if(st[12] == 0)
{
mv[c].x=12;
mv[c].y=14;
c++;
}
if(st[8] == 0)
{
mv[c].x=8;
mv[c].y=14;
c++;
}
}
if(st[6])
{
if(st[1] == 0)
{
mv[c].x=1;
mv[c].y=6;
c++;
if(st[0] == 0)
{
mv[c].x=0;
mv[c].y=6;
c++;
}
if(st[2] == 0)
{
mv[c].x=2;
mv[c].y=6;
c++;
}
}
if(st[11] == 0)
{
mv[c].x=11;
mv[c].y=6;
c++;
if(st[10] == 0)
{
mv[c].x=10;
mv[c].y=6;
c++;
}
if(st[12] == 0)
{
mv[c].x=12;
mv[c].y=6;
c++;
}
}
}
if(st[8])
{
if(st[3] == 0)
{
mv[c].x=3;
mv[c].y=8;
c++;
if(st[4] == 0)
{
mv[c].x=4;
mv[c].y=8;
c++;
}
if(st[2] == 0)
{
mv[c].x=2;
mv[c].y=8;
c++;
}
}
if(st[13] == 0)
{
mv[c].x=13;
mv[c].y=8;
c++;
if(st[14] == 0)
{
mv[c].x=14;
mv[c].y=8;
c++;
}
if(st[12] == 0)
{
mv[c].x=12;
mv[c].y=8;
c++;
}
}
}
return c;
}
/* */
long move(PZ_NODE *n1, PZ_MOVE *mv, PZ_NODE *n2) //走一步,返回走一步后的结果
{
char ss[NUM];
RTRANS(n1->v, ss);
XCHG(ss[mv->x], ss[mv->y]);
TRANS(ss, n2->v);
return 0;
}
/* */
long add_node1(PZ_NODE *node, long r)
{
PZ_NODE *p=m_root1;
PZ_NODE *q;
while(p)
{
q=p;
if(p->v == node->v)
return 0;
else if(node->v > p->v)
p=p->big;
else if(node->v < p->v)
p=p->small;
}
m_ar1[r].v=node->v;
m_ar1[r].prev=node->prev;
m_ar1[r].small=NULL;
m_ar1[r].big=NULL;
if(node->v > q->v)
{
q->big= &m_ar1[r];
}
else if(node->v < q->v)
{
q->small= &m_ar1[r];
}
return 1;
}
/* */
long add_node2(PZ_NODE *node, long r)
{
PZ_NODE *p=m_root2;
PZ_NODE *q;
while(p)
{
q=p;
if(p->v == node->v)
return 0;
else if(node->v > p->v)
p=p->big;
else if(node->v < p->v)
p=p->small;
}
m_ar2[r].v=node->v;
m_ar2[r].prev=node->prev;
m_ar2[r].small=NULL;
m_ar2[r].big=NULL;
if(node->v > q->v)
{
q->big= &m_ar2[r];
}
else if(node->v < q->v)
{
q->small= &m_ar2[r];
}
return 1;
}
/*
得到节点所在深度
*/
long get_node_depth(PZ_NODE *node)
{
long d=0;
while(node->prev)
{
d++;
node=node->prev;
}
return d;
}
/*
到树2中搜索此节点是否存在
*/
PZ_NODE *check_ok1(PZ_NODE *node)
{
PZ_NODE *p=m_root2;
while(p)
{
if(p->v == node->v)
return p;
else if(node->v > p->v)
p=p->big;
else if(node->v < p->v)
p=p->small;
}
return NULL;
}
/*
到树1中搜索此节点是否存在
*/
PZ_NODE *check_ok2(PZ_NODE *node)
{
PZ_NODE *p=m_root1;
while(p)
{
if(p->v == node->v)
return p;
else if(node->v > p->v)
p=p->big;
else if(node->v < p->v)
p=p->small;
}
return NULL;
}
/*
返回值:成功-返回1,节点数不够-(-1),无解-(-2)
*/
long bfs_search(char *begin, char *end)
{
long h1=0, r1=1, h2=0, r2=1, c, i;
PZ_NODE node, *pnode;
PZ_MOVE mv[MAX_MOVE]; //每个局面最多4种走法
TRANS(end, m_ar2[0].v);
TRANS(begin, m_ar1[0].v);
m_ar1[0].prev=NULL;
m_root1=m_ar1;
m_root1->small=NULL;
m_root1->big=NULL;
m_ar2[0].prev=NULL;
m_root2=m_ar2;
m_root2->small=NULL;
m_root2->big=NULL;
while((h1 < r1) && (r1 < MAX_NODE - MAX_MOVE))
{
c=move_gen(&m_ar1[h1], mv);
for(i=0; i < c; i++)
{
move(&m_ar1[h1], &mv[i], &node);
node.prev= &m_ar1[h1];
if(add_node1(&node, r1)) //只能对历史节点中没有的新节点搜索,否则会出现环
{
r1++;
if((pnode=check_ok1(&node)) != NULL)
{
m_depth2=0;
while(pnode)
{
m_out2[m_depth2++].v=pnode->v;
pnode=pnode->prev;
}
pnode= &node;
m_depth1=0;
while(pnode)
{
m_out1[m_depth1++].v=pnode->v;
pnode=pnode->prev;
}
return 1;
}
}
}
h1++;
c=move_gen(&m_ar2[h2], mv);
for(i=0; i < c; i++)
{
move(&m_ar2[h2], &mv[i], &node);
node.prev= &m_ar2[h2];
if(add_node2(&node, r2)) //只能对历史节点中没有的新节点搜索,否则会出现环
{
r2++;
if((pnode=check_ok2(&node)) != NULL)
{
m_depth1=0;
while(pnode)
{
m_out1[m_depth1++].v=pnode->v;
pnode=pnode->prev;
}
pnode= &node;
m_depth2=0;
while(pnode)
{
m_out2[m_depth2++].v=pnode->v;
pnode=pnode->prev;
}
return 1;
}
}
}
h2++;
printf("/rSearch... %d/%d @ %d | %d/%d @ %d",
h1,
r1,
get_node_depth(&m_ar1[h1]),
h2,
r2,
get_node_depth(&m_ar2[h2]));
}
if((h1 == r1) && (h2 == r2))
{
return -2;
}
else
{
return -1;
}
}
/* */
void output(void)
{
long i, j, k;
char ss[NUM];
for(i=m_depth1 - 1; i >= 0; i--)
{
RTRANS(m_out1[i].v, ss);
for(j=0; j < ROW; j++)
{
for(k=0; k < COL; k++)
{
printf("%3d", ss[COL * j + k]);
}
printf("/n");
}
printf("/n");
}
for(i=1; i < m_depth2; i++)
{
RTRANS(m_out2[i].v, ss);
for(j=0; j < ROW; j++)
{
for(k=0; k < COL; k++)
{
printf("%3d", ss[COL * j + k]);
}
printf("/n");
}
printf("/n");
}
}
/* */
int main(void)
{
#if 1
char begin[NUM]={ 1, 2, 3, 4, 5, 15, 0, 15, 0, 15, 6, 7, 8, 9, 10 }; //0表示空格,15表示墙
char end[NUM]={ 6, 7, 8, 9, 10, 15, 0, 15, 0, 15, 1, 2, 3, 4, 5 };
#else
char begin[NUM]={ 1, 1, 1, 1, 1, 15, 0, 15, 0, 15, 2, 2, 2, 2, 2 }; //0表示空格,15表示墙
char end[NUM]={ 2, 2, 2, 2, 2, 15, 0, 15, 0, 15, 1, 1, 1, 1, 1 };
#endif
long r=bfs_search(begin, end);
printf("/n");
if(r >= 0)
{
output();
}
else
{
printf("bfs_search returns %d/n", r);
}
return 0;
}