双向广度优先搜索法。

上一篇文章里讲到了广度优先搜索法,并以8数码问题为例。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;
}

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页