A*算法

A*算法八数码问题

  • 八数码问题描述

    八数码问题也称为九宫问题。在3×3的棋盘,摆有八个棋子,每个棋子上标有1至8的某一数字,不同棋子上标的数字不相同。棋盘上还有一个空格,与空格相邻的棋子可以移到空格中,这里空格用数字0代替。给定九宫格的初始状态重点内容,要求在有限步的操作内,使其转化为目标状态,且所得到的解是代价最小解(即移动的步数最少)并打印出求解路径及求解的扩展节点。

  • A*算法

    (1)A*算法;是对A算法的估价函数f(n)=g(n)+h(n)加上某些限制后得到的一种启发式搜索算法。A*算法对A算法中的g(n)和h(n)分别提出如下限制:第一,g(n)是对最小代价g*(n)的估计,且g(n)>0;第二,h(n)是最小代价h*(n)的下界,即对任意节点n 均有h(n)≤h*(n)。即满足上述两条限制的A算法称为A*算法。
    (2)估价函数;用来估算节点希望程度的量度,叫估价函数f(x),f(x)=g(x)+h(x)。g(x)为从初始节点到当前节点已经付出的代价,h(x)为从当前节点到目标节点的最优路径的估计代价。本算法中令g(x)为当前节点的深度depth;
    h(x)为当前节点每个数字位与目标节点数字位间距离和distance,进一步考虑当前结点与目标结点的距离信息,可以令启发函数h ( n )为当前8个数字位与目标结点对应数字位距离和(不考虑中间路径),满足h ( n ) <= h * ( n ), 且对于目标节点有 h ( t ) = 0,对于结点m和n (n 是m的子结点) 有h ( m ) – h ( n ) <= 1满足单调限制条件;也可以考虑令启发函数h ( n )为放错棋子移到目标位置的距离的总和;当然也可以如A算法令启发函数h ( n )为放错棋子的数目;
    在这里使用的启发函数h ( n )为w ( n ) +3*d ( n ),w ( n )为放错棋子的数目,
    d ( n )为放错棋子移到目标位置的距离的总和;
    (3)open和closed表的数据结构表示;对open表的操作,每次需要得到所有待扩展结点中 f 值最小的那个结点。closed表存储已扩展的结点间的扩展关系,主要用于输出路径。closed 表中任意一个结点都存储有它的前驱结点的信息,考虑closed表中任意一个结点,如果它是初始结点,它没有前驱结点,如果不是根结点,扩展该结点时它的前驱结点已经记录。从而在closed表中形成扩展关系的树状结构。因为只需要前驱点的下标位置,可以用数组实现。每个结点记录8数码格局和它的前驱结点的下标。

  • 算法分析

    1. 把S放入open表,记f = h,令closed为空表;
    2. loop:直至找到目标节点为止。若open表为空,则失败;
    3. 选取open表中(未设置过的)具有最小f的节点为最佳节点bes,并把它放入closed表;
    4. 若bes为一目标节点,则成功求得一解;
    5. 若bes不是目标节点,则扩展之,产生后继节点suc;
    6. 对每个后继节点suc,进行下列过程:
      a. 建立从后继节点suc返回最佳节点bes的指针;
      b. 计算g(suc) = g(bes) + g(bes,suc);
      c. 若后继节点suc在open表或clsoed表中,则称此节点为old,并把它添加至最佳节点bes的后继节点表中,比较新旧路径代价:
      1) 若g(suc)小于g(old),则重新确定old的父辈节点为bes节点,记下较小代价
      g(old),并修正f(old)值。转7;
      2) 若至old节点的代价较低或一样,则停止可扩展节点;转7;
    7. 计算f的值;
    8. Go loop
  • 源程序
    程序是用C实该程序实现了A*算法寻找最优的路径,并且把所有的扩展节点给打印出来,打印扩展节点时,运行程序得到的结果中从上至下,第一大列对应的便是解决方案的路径,同时也是父节点与子节点之间的关系,
    Step n 这一代的所有节点对应的父节点都是Step n-1 代节点中最左边的那个节点
    棋子目标状态:
    1 2 3
    8 0 4
    7 6 5

  • 程序实现如下 :


#include
#include
#include
struct node
{
    int start[3][3];
    int f;    //估价函数
    int g;    //节点深度
    int h;    //启发函数
    int x;    //空格的位置
    int y;
    struct node * father;
    struct node * next;
};
static int target[3][3] = { 1,2,3,8,0,4,7,6,5 };//目标状态
static int sum_num = 0;
static int same[100];

int gets_h(node*s);
int same_node(node*a, node*b);//节点是否相等
node*In_list(node*L, node*n);//节点n是否在L表中

void zero_place(node*s)
{
    int i, j;
    for (i = 0; i < 3; i++)
    {
        for (j = 0; j < 3; j++)
        {
            if (s->start[i][j] == 0)
            {
                s->x = i;
                s->y = j;
                return;
            }
        }
    }
}
void _input(node* s)
{
    int i, j;
    printf("测试情况:输入\n\n");
    printf("****2 8 3****\n");
    printf("****1 6 4****\n");
    printf("****7 0 5****\n\n");
    printf("请输入初始位置:(3*3)\n");
    for (i = 0; i < 3; i++)
    {
        for (j = 0; j < 3; j++)
        {
            scanf("%d", &s->start[i][j]);
        }
        printf("\n");
    }
    s->g = 0;
    s->h = gets_h(s);
    s->f = s->g + s->h;
    zero_place(s);
    s->next = NULL;
    s->father = NULL;
    return;
}
void _print(node*best, node*all)
{
    int i;
    int j;
    int k;
    int length = 0;//same数组不为0的个数
    for (i = 0; i < 100; i++)
    {
        if (same[i] != 0)
        {
            length++;
        }
    }
    node *s[400];//对应best
    node *ss[400];//对应all
    node *child;
    node *other;
    child = best;
    other = all;
    for (i = 0; i < 400; i++)
    {
        s[i] = NULL;
        ss[i] = NULL;
    }
    int depth = child->g;
    for (i = depth; i >= 0; i--)
    {
        s[i] = child;
        child = child->next;
    }
    for (i = length; i > 0; i--)
    {
        ss[i - 1] = other;
        other = other->next;
    }
    printf("扩展%d层\n", depth);
    int m;
    //int n = 0;
    for (k = 0; k <= depth; k++)
    {
        printf("Step %d\n", k);

        for (i = 0; i < 3; i++)
        {
            for (j = 0; j < 3; j++)
            {
                printf("%d\t", s[k]->start[i][j]);
            }
            for (m = 0; m < length; m++)//判断是否有相同f值的扩展节点
            {
                if (k == same[m] && k != 0)//有同f的节点
                {
                    printf("\t");
                    for (j = 0; j < 3; j++)
                    {
                        printf("%d\t", ss[m]->start[i][j]);
                    }
                    //n++;
                }
            }
            printf("\n");
        }
    }
}
int gets_h(node *s)    //获取启发函数的值
{
    int i;
    int j;
    int k = 0;
    int value = 0;
    for (i = 0; i < 3; i++)
    {
        for (j = 0; j < 3; j++)
        {
            if (s->start[i][j] != target[i][j] && s->start[i][j] != 0)
            {
                value++;
            }
        }
    }
    k = value;
    value = 0;
    int a[8][2], b[8][2];
    for (i = 0; i < 3; i++)
    {
        for (j = 0; j < 3; j++)
        {
            if (s->start[i][j] != 0)
            {
                a[s->start[i][j] - 1][0] = i;// { i, j };
                a[s->start[i][j] - 1][1] = j;
            }
            if (target[i][j] != 0)
            {
                b[target[i][j] - 1][0] = i;// { i, j };
                b[target[i][j] - 1][1] = j;
            }
        }
    }
    for (i = 0; i < 8; i++)
    {
        for (j = 0; j < 2; j++)
        {
            value = value + abs(a[i][j] - b[i][j]);
        }
    }
    value = (value * 3 + k);
    return value;
}
void list_insert(node* &List, node *n)//插入新节点
{
    if (List == NULL)
    {
        n->next = NULL;
        List = n;
    }
    else
    {
        n->next = List;
        List = n;
    }
    return;
}
void list_remove(node* &L, node*n)//从链表L中移除节点n
{
    struct node *p, *q;
    p = NULL, q = NULL;
    if (L == NULL)//链表L为空
    {
        return;
    }
    else if (same_node(L, n) && L->f == n->f)//链表L的表头节点位n
    {
        L = L->next;
        L->next = NULL;
        return;
    }
    else
    {
        p = L;
        q = p->next;
        while (q)
        {
            if (same_node(q, n) && q->f == n->f)
            {
                p->next = q->next;
                return;
            }
            else
            {
                p = q;
                q = q->next;
            }
        }
        return;
    }
}
void getmin(node* &L, node*p)//p插入open表中,寻找f最小的节点,并对open表进行递增的排序
{
    struct node *p1, *p2;
    p1 = L;
    p2 = NULL;
    if (L == NULL)
    {
        p->next = NULL;
        L = p;
        //sum_num++;
    }
    else
    {
        while (p1 != NULL&&p->f > p1->f)
        {
            p2 = p1;//p2始终指向p1指向的前一个元素
            p1 = p1->next;
            //sum_num++;
        }
        if (p2 == NULL)      //待插入节点为当前open表最小
        {
            p->next = L;
            L = p;
        }
        else if (p1 == NULL)  //待插入节点为当前open表最大
        {
            p->next = NULL;
            p2->next = p;
        }
        else               //待插入节点介于p2、p1之间
        {
            p2->next = p;
            p->next = p1;
        }
    }
}
void search(node *L, node * &all)
{
    int i;
    node *s2;
    node *s1[4];
    if (L != NULL)
    {
        for (i = 0; i < 4; i++)
        {
            s1[i] = NULL;
        }
        s2 = L;
        for (i = 0; i < 4 && s2 != NULL; i++)
        {
            s1[i] = s2;
            s2 = s2->next;
        }
        sum_num++;
        //list_insert(all, s1[0]);
        for (i = 1; i < 4 && s1[i] != NULL; i++)
        {
            if (s1[0]->h == s1[i]->h)
            {
                sum_num++;
                same[sum_num - s1[0]->g - 1] = s1[0]->g;
                list_insert(all, s1[i]);
            }
        }
    }
    return;
}
void get_node(node* &open, node* close, node *bestnode, int deep, int move, int &sumnode)//扩展新节点,空格移动
{
    struct node *newnode, *f_node, *old, *ls;
    old = NULL;
    ls = NULL;
    newnode = NULL;
    newnode = (struct node*)malloc(sizeof(struct node));
    f_node = bestnode->father;//当前节点的父节点
    if (f_node != NULL&&f_node->x == bestnode->x&&f_node->y == bestnode->y)//防止回溯父节点
    {
        ;
    }
    else
    {
        int i, j;
        //int x, y;
        for (i = 0; i < 3; i++)
        {
            for (j = 0; j < 3; j++)
            {
                newnode->start[i][j] = bestnode->start[i][j];
            }
        }
        newnode->x = bestnode->x;
        newnode->y = bestnode->y;
        newnode->next = bestnode->next;
        newnode->father = bestnode->father;
        switch (move)
        {
        case 1://左移
            newnode->start[newnode->x][newnode->y] = newnode->start[newnode->x][newnode->y - 1];
            newnode->start[newnode->x][newnode->y - 1] = 0;
            newnode->y = newnode->y - 1;//y-1
            break;
        case 2://右移
            newnode->start[newnode->x][newnode->y] = newnode->start[newnode->x][newnode->y + 1];
            newnode->start[newnode->x][newnode->y + 1] = 0;
            newnode->y = newnode->y + 1;//y+1
            break;
        case 3://上移
            newnode->start[newnode->x][newnode->y] = newnode->start[newnode->x - 1][newnode->y];
            newnode->start[newnode->x - 1][newnode->y] = 0;
            newnode->x = newnode->x - 1;//x-1
            break;
        case 4://下移
            newnode->start[newnode->x][newnode->y] = newnode->start[newnode->x + 1][newnode->y];
            newnode->start[newnode->x + 1][newnode->y] = 0;
            newnode->x = newnode->x + 1;//x+1
            break;
        default:
            break;
        }
        newnode->g = deep + 1;
        newnode->h = gets_h(newnode);
        newnode->f = newnode->g + newnode->h;//新节点的估价值
                                             //bestnode->next = newnode;
        newnode->father = bestnode;
        ls = newnode;
        ls->father = NULL;
        ls->next = NULL;
        if (old = In_list(open, newnode))//后继节点newnode在open表中
        {
            if (newnode->g < old->g)
            {
                list_remove(open, old);
                getmin(open, ls);
                sumnode++;
            }
        }
        else if (old = In_list(close, newnode))//后继节点newnode在close表中
        {
            if (newnode->g < old->g)
            {
                list_remove(close, old);
                getmin(open, ls);
                sumnode++;
            }
        }
        else //后继节点放进open表,并添加到bestnode的后裔表中
        {
            getmin(open, ls);
            sumnode++;
        }
    }
}
int same_node(node*a, node*b)//节点是否相等
{
    int i, j;
    int flag = 1;
    for (i = 0; i < 3; i++)
    {
        for (j = 0; j < 3; j++)
        {
            if (a->start[i][j] != b->start[i][j])
            {
                flag = 0;
                return flag;
            }
        }
    }
    return flag;
}
node * In_list(node*L, node*n)//节点n是否在L表中
{
    struct node*temp;
    struct node *ls;
    ls = NULL;
    temp = L;
    //int i = 0;
    //if (L == NULL)
    //  printf("edfa\n");
    while (temp != NULL)
    {
        if (same_node(n, temp))
        {
            ls = temp;
            break;
        }
        else
        {
            temp = temp->next;
        }
    }
    //free(temp);
    return ls;
}
void main()
{
    for (int i = 0; i < 100; i++)
    {
        same[i] = 0;
    }
    int result = 0;
    int sunnode = 0;//扩展节点数
    struct node* open = NULL;//open表
                             //open->father = NULL;
    struct node* close = NULL;//close表
    struct node s;//初始结点
    struct node *bestnode;
    struct node *best;
    best = NULL;
    struct node *current;
    struct node *a;
    a = NULL;
    struct node *all;
    all = NULL;
    _input(&s);
    list_insert(open, &s);
    //list_insert(all, &s);
    while (open != NULL)
    {
        bestnode = open;
        a = bestnode;
        a->next = NULL;
        //current = NULL;
        current = open->next;
        list_insert(close, a);
        list_insert(best, a);
        open = current;//open表中移出bestnode节点
        if (bestnode->h == 0)
        {
            result = 1;
            printf("寻找成功\n");
            break;
        }
        else
        {
            //把可扩展节点放到open表中
            if (bestnode->y >= 1)//左移
            {
                get_node(open, close, bestnode, bestnode->g, 1, sunnode);
            }
            if (bestnode->y <= 1)//右移
            {
                get_node(open, close, bestnode, bestnode->g, 2, sunnode);
            }
            if (bestnode->x >= 1)//上移
            {
                get_node(open, close, bestnode, bestnode->g, 3, sunnode);
            }
            if (bestnode->x <= 1)//下移
            {
                get_node(open, close, bestnode, bestnode->g, 4, sunnode);
            }       
            search(open, all);
        }
    }
    if (result == 1)
    {
        _print(best, all);
        printf("搜索过的叶子节点:%d\n", sunnode);
        printf("扩展节点数目:%d\n", sum_num);
    }
    else
    {
        printf("没达到目标\n");
    }
}
  • 测试结果1:
    -初始状态:
    2 8 3
    1 6 4
    7 0 5
    运行;
    测试情况:输入

*2 8 3*
*1 6 4*
*7 0 5*

请输入初始位置:(3*3)
2 8 3

1 6 4

7 0 5

寻找成功
扩展5层
Step 0
2 8 3
1 6 4
7 0 5
Step 1
2 8 3
1 0 4
7 6 5
Step 2
2 0 3
1 8 4
7 6 5
Step 3
0 2 3
1 8 4
7 6 5
Step 4
1 2 3
0 8 4
7 6 5
Step 5
1 2 3
8 0 4
7 6 5
搜索过的叶子节点:11
扩展节点数目:5
请按任意键继续…
- 测试结果2:
-初始状态:
0 3 4
5 1 6
7 8 2
运行;
测试情况:输入

*2 8 3*
*1 6 4*
*7 0 5*

请输入初始位置:(3*3)
0 3 4

5 1 6

7 8 2

寻找成功
扩展56层
Step 0
0 3 4
5 1 6
7 8 2
Step 1
5 3 4 3 0 4
0 1 6 5 1 6
7 8 2 7 8 2
Step 2
5 3 4
1 0 6
7 8 2
Step 3
5 3 4 5 3 4
1 8 6 1 6 0
7 0 2 7 8 2
Step 4
5 3 4
1 8 6
7 2 0
Step 5
5 3 4
1 8 0
7 2 6
……
……
……
Step 51
1 0 3
8 2 6
7 5 4
Step 52
1 2 3
8 0 6
7 5 4
Step 53
1 2 3
8 6 0
7 5 4
Step 54
1 2 3
8 6 4
7 5 0
Step 55
1 2 3
8 6 4
7 0 5
Step 56
1 2 3
8 0 4
7 6 5
搜索过的叶子节点:105
扩展节点数目:66
请按任意键继续. . .
以上程序也是刚学人工智能A*算法写的,有不对的地方我们共同交流;

你可能感兴趣的:(C/C++,人工智能)