A*算法

    xiaoxiao2021-04-15  148

    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数码格局和它的前驱结点的下标。

    算法分析

    把S放入open表,记f = h,令closed为空表;loop:直至找到目标节点为止。若open表为空,则失败;选取open表中(未设置过的)具有最小f的节点为最佳节点bes,并把它放入closed表;若bes为一目标节点,则成功求得一解;若bes不是目标节点,则扩展之,产生后继节点suc;对每个后继节点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;计算f的值;Go loop

    源程序 程序是用C实该程序实现了A*算法寻找最优的路径,并且把所有的扩展节点给打印出来,打印扩展节点时,运行程序得到的结果中从上至下,第一大列对应的便是解决方案的路径,同时也是父节点与子节点之间的关系, Step n 这一代的所有节点对应的父节点都是Step n-1 代节点中最左边的那个节点 棋子目标状态: 1 2 3 8 0 4 7 6 5

    程序实现如下 :


    #include<stdio.h> #include<stdlib.h> #include<malloc.h> 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*算法写的,有不对的地方我们共同交流;

    转载请注明原文地址: https://ju.6miu.com/read-670944.html

    最新回复(0)