八数码问题A算法的实现及性能分析
A星算法求解八数码技术报告

A*算法求解八数码问题●open 表、closed 表数据结构的选择:1)把s放入open表,记f=h,令closed为空表。
2)重复下列过程,直到找到目标节点为止。
若open表为空表,则宣告失败。
3)选取open表中未设置过的具有最小f值的节点为最佳节点bestnode,并把它放入closed表。
4)若bestnode为一目标节点,则成功求得一解。
5)若bestnode不是目标节点,则扩展之,产生后继节点succssor。
6)对每个succssor进行下列过称:a)对每个succssor返回bestnode的指针。
b)计算g(suc)=g(bes)+k(bes,suc)。
c)如果succssore open,称此节点为old,并填到bestnode的后继节点表中。
d)比较新旧路劲代价。
如果g(suc)<g(old),则重新确定old的父辈节点为bestnode,记下较小代价g(old),并修真f(old)值。
e)若至old节点的代价较低或一样,则停止扩展节点。
f)若succssore不再closed表中,则看其是否在closed表中。
g)若succssore在closed表中,则转向(c)。
h)若succssore既不在open表中,又不在closed表中,则把它放入open表中,并添入bestnode后裔表中,然后转向(7)。
i)计算f值j)Go loop●节点的数据结构:static int target[9]={1,2,3,8,0,4,7,6,5}; 全局静态变量,表示目标状态class eight_num{private:int num[9]; 定义八数码的初始状态int not_in_position_num; 定义不在正确位置八数码的个数int deapth; 定义了搜索的深度int eva_function; 评价函数的值,每次选取最小的进行扩展public:eight_num* parent; 指向节点的父节点eight_num* leaf_next; 指向open表的下一个节点eight_num* leaf_pre; 指向open 表的前一个节点初始状态的构造函数eight_num(int init_num[9]);eight_num(int num1,int num2,int num3,int num4,int num5,int num6,int num7,int num8,int num9){}eight_num(void){ }计算启发函数g(n)的值void eight_num::cul_para(void){}显示当前节点的状态void eight_num::show(){}复制当前节点状态到一个另数组中void eight_num::get_numbers_to(int other_num[9]){}设置当前节点状态(欲设置的状态记录的other数组中)void eight_num::set_num(int other_num[9]){}eight_num& eight_num::operator=(eight_num& another_8num){} eight_num& eight_num::operator=(int other_num[9]){}int eight_num::operator==(eight_num& another_8num){}int eight_num::operator==(int other_num[9]){}空格向上移int move_up(int num[9]){}空格向下移int move_down(int num[9]){}空格向左移int move_left(int num[9]){}空格向右移int move_right(int num[9]){}判断可否解出int icansolve(int num[9],int target[9]){}判断有无重复int existed(int num[9],eight_num *where){}寻找估价函数最小的叶子节点eight_num* find_OK_leaf(eight_num* start){}}A*算法求解框图:●分析估价函数对搜索算法的影响:估价函数就是评价函数,它用来评价子结点的好坏,因为准确评价是不可能的,所以称为估值。
A星算法求八数码问题实验报告

A星算法求八数码问题实验报告人工智能实验报告实验名称:八数码问题姓名:xx学号:2012210xxxx计算机学院2014年1月14日一.实验目的掌握A*的思想,启发式搜索,来求解在代价最小的情况下将九宫格从一个状态转为另状态的路径。
二.实验内容给定九宫格的初始状态,要求在有限步的操作内,使其转化为目标状态,且所得到的解是代价最小解(2 8 31 6 47 0 52 8 31 6 47 0 5三、A*算法思想:1、思想:A*算法是一种静态路网中求解最短路最有效的直接搜索方法。
估价值与实际值越接近,估价函数取得就越好2、原理:估价函数公式表示为: f(n)=g(n)+h(n),其中 f(n) 是从初始点经由节点n到目标点的估价函数,g(n) 是在状态空间中从初始节点到n节点的实际代价,h(n) 是从n到目标节点最佳路径的估计代价。
保证找到最短路径(最优解的)条件,关键在于估价函数h(n)的选取:估价值h(n)<= n到目标节点的距离实际值,这种情况下,搜索的点数多,搜索范围大,效率低。
但能得到最优解。
并且如果h(n)=d(n),即距离估计h(n)等于最短距离,那么搜索将严格沿着最短路径进行此时的搜索效率是最高的。
如果估价值>实际值,搜索的点数少,搜索范围小,效率高,但不能保证得到最优解。
四、算法流程:Heuristic_Search(启发式搜索)While是从未拓展表中删N为目是,输出路径否,生成n的所有子状态Case:此子状Case:此子状Case:此子状计算该子状记录比已有记录比已有返回五、关键技术:1、使用了CreateChild()函数,求得了任意未拓展九宫格的扩展结点,便于拓展子空间,搜索所有情况。
关键代码:bool CreateChild(NOExtend ns[],NOExtend ne){int i,j,k=0;for(i=0;i<3;i++){for(j=0;j<3;j++){if(ne.cur_sudoku.num[i][j]==0){ //寻找九宫格空缺所在的坐标if(i-1>=0){ //将空格向上移动CopySudoku(ns[k].cur_sudoku,ne.cur_sudo ku);//先把未改变的九宫格复制给九宫格数组的某一元素ns[k].cur_sudoku.num[i][j]=ne.cur_sudoku.num[i-1][j];//然后仅改变此二维九宫格的两项值即可ns[k].cur_sudoku.num[i-1][j]=0;ns[k].dx=1;k++;}if(j+1<=2){ //将空格向右移动CopySudoku(ns[k].cur_sudoku,ne.cur_sudo ku);ns[k].cur_sudoku.num[i][j]=ns[k].cur_su doku.num[i][j+1];ns[k].cur_sudoku.num[i][j+1]=0;ns[k].dx=1;k++;}if(i+1<=2){ //将空格向下移动CopySudoku(ns[k].cur_sudoku,ne.cur_sudo ku);ns[k].cur_sudoku.num[i][j]=ns[k].cur_su doku.num[i+1][j];ns[k].cur_sudoku.num[i+1][j]=0;ns[k].dx=1;k++;}if(j-1>=0){ //将空格向左移动CopySudoku(ns[k].cur_sudoku,ne.cur_sudo ku);ns[k].cur_sudoku.num[i][j]=ns[k].cur_su doku.num[i][j-1];ns[k].cur_sudoku.num[i][j-1]=0;ns[k].dx=1;k++;}return 1;}}}return 0;2、用启发式搜索函数寻找求解路径,运用了A*算法的思想,能够更快的求解出最优解。
a星算法求解八数码问题python

a星算法求解八数码问题python一、介绍八数码问题是一种经典的智力游戏,也是人工智能领域中的经典问题之一。
在这个问题中,有一个3×3的棋盘,上面摆着1至8这8个数字和一个空格,初始状态和目标状态都已知。
要求通过移动数字,将初始状态变换成目标状态。
其中空格可以和相邻的数字交换位置。
为了解决这个问题,我们可以使用A*算法。
本文将详细介绍如何用Python实现A*算法来求解八数码问题。
二、A*算法简介A*算法是一种启发式搜索算法,常用于寻找最短路径或最优解等问题。
它基于Dijkstra算法,并加入了启发式函数来加速搜索过程。
在A*算法中,每个节点都有两个估价值:g值和h值。
g值表示从起点到该节点的实际代价,h值表示从该节点到目标节点的估计代价。
启发式函数f(n) = g(n) + h(n) 表示从起点到目标节点的估计总代价。
A*算法采用优先队列来保存待扩展的节点,并按照f(n)值从小到大排序。
每次取出队头元素进行扩展,并将扩展出来的新节点按照f(n)值插入队列中。
当扩展出目标节点时,算法结束。
三、八数码问题的状态表示在八数码问题中,每个状态都可以表示为一个3×3的矩阵。
我们可以用一个一维数组来表示这个矩阵,其中0表示空格。
例如,初始状态可以表示为[2, 8, 3, 1, 6, 4, 7, 0, 5],目标状态可以表示为[1, 2, 3, 8, 0, 4, 7, 6, 5]。
四、A*算法求解八数码问题的步骤1.将初始状态加入优先队列中,并设置g值和h值为0。
2.从队头取出一个节点进行扩展。
如果该节点是目标节点,则搜索结束;否则,将扩展出来的新节点加入优先队列中。
3.对于每个新节点,计算g值和h值,并更新f(n)值。
如果该节点已经在优先队列中,则更新其估价值;否则,将其加入优先队列中。
4.重复第2步至第3步直到搜索结束。
五、Python实现以下是用Python实现A*算法求解八数码问题的代码:```import heapqimport copy# 目标状态goal_state = [1,2,3,8,0,4,7,6,5]# 启发式函数:曼哈顿距离def h(state):distance = 0for i in range(9):if state[i] == 0:continuerow = i // 3col = i % 3goal_row = (state[i]-1) // 3goal_col = (state[i]-1) % 3distance += abs(row - goal_row) + abs(col - goal_col)return distance# A*算法def A_star(start_state):# 初始化优先队列和已访问集合queue = []visited = set()# 将初始状态加入优先队列中,并设置g值和h值为0heapq.heappush(queue, (h(start_state), start_state, 0))while queue:# 取出队头元素进行扩展f, state, g = heapq.heappop(queue)# 如果该节点是目标节点,则搜索结束;否则,将扩展出来的新节点加入优先队列中。
八数码问题 实验报告

八数码问题实验报告八数码问题实验报告引言:八数码问题是一种经典的数学难题,在计算机科学领域有着广泛的研究和应用。
本实验旨在通过探索八数码问题的解法,深入理解该问题的本质,并通过实验结果评估不同算法的效率和准确性。
一、问题描述:八数码问题是一个在3×3的棋盘上,由1至8的数字和一个空格组成的拼图问题。
目标是通过移动棋盘上的数字,使得棋盘上的数字排列按照从小到大的顺序排列,最终形成如下的目标状态:1 2 34 5 67 8二、解法探索:1. 深度优先搜索算法:深度优先搜索算法是一种经典的解决拼图问题的方法。
该算法通过不断尝试所有可能的移动方式,直到找到目标状态或者无法再继续移动为止。
实验结果显示,该算法在八数码问题中能够找到解,但由于搜索空间庞大,算法的时间复杂度较高。
2. 广度优先搜索算法:广度优先搜索算法是另一种常用的解决八数码问题的方法。
该算法通过逐层扩展搜索树,从初始状态开始,逐步扩展所有可能的状态,直到找到目标状态。
实验结果显示,该算法能够找到最短路径的解,但同样面临搜索空间庞大的问题。
3. A*算法:A*算法是一种启发式搜索算法,结合了深度优先搜索和广度优先搜索的优点。
该算法通过使用一个估价函数来评估每个搜索状态的优劣,并选择最有希望的状态进行扩展。
实验结果显示,A*算法在八数码问题中表现出色,能够高效地找到最优解。
三、实验结果与分析:通过对深度优先搜索、广度优先搜索和A*算法的实验,得出以下结论:1. 深度优先搜索算法虽然能够找到解,但由于搜索空间庞大,时间复杂度较高,不适用于大规模的八数码问题。
2. 广度优先搜索算法能够找到最短路径的解,但同样面临搜索空间庞大的问题,对于大规模问题效率较低。
3. A*算法在八数码问题中表现出色,通过合理的估价函数能够高效地找到最优解,对于大规模问题具有较好的效果。
四、结论与展望:本实验通过对八数码问题的解法探索,深入理解了该问题的本质,并评估了不同算法的效率和准确性。
实验三A星算法求解数码问题实验

实验三:A算法求解8数码问题实验一、实验目的熟悉和掌握启发式搜索的定义、估价函数和算法过程;并利用A算法求解N数码难题;理解求解流程和搜索顺序..二、实验内容1、八数码问题描述所谓八数码问题起源于一种游戏:在一个3×3的方阵中放入八个数码1、2、3、4、5、6、7、8;其中一个单元格是空的..将任意摆放的数码盘城初始状态逐步摆成某个指定的数码盘的排列目标状态;如图1所示图1 八数码问题的某个初始状态和目标状态对于以上问题;我们可以把数码的移动等效城空格的移动..如图1的初始排列;数码7右移等于空格左移..那么对于每一个排列;可能的一次数码移动最多只有4中;即空格左移、空格右移、空格上移、空格下移..最少有两种当空格位于方阵的4个角时..所以;问题就转换成如何从初始状态开始;使空格经过最小的移动次数最后排列成目标状态..2、八数码问题的求解算法2.1 盲目搜索宽度优先搜索算法、深度优先搜索算法2.2 启发式搜索启发式搜索算法的基本思想是:定义一个评价函数f;对当前的搜索状态进行评估;找出一个最有希望的节点来扩展..先定义下面几个函数的含义:fn=gn+hn 1式中gn表示从初始节点s到当前节点n的最短路径的耗散值;hn 表示从当前节点n到目标节点g的最短路径的耗散值;fn表示从初始节点s经过n到目标节点g的最短路径的耗散值..评价函数的形式可定义如2式所示:fn=gn+hn 2其中n是被评价的当前节点..fn、gn和hn分别表示是对fn、gn和hn3个函数值的估计值..利用评价函数fn=gn+hn来排列OPEN表节点顺序的图搜索算法称为算法A..在A算法中;如果对所有的x;hx<=hx 3成立;则称好hx为hx的下界;它表示某种偏于保守的估计..采用hx 的下界hx为启发函数的A算法;称为A算法..针对八数码问题启发函数设计如下:fn=dn+pn 4其中A算法中的gn根据具体情况设计为dn;意为n节点的深度;而hn设计为图2 A算法流程图pn;意为放错的数码与正确的位置距离之和..由于实际情况中;一个将牌的移动都是单步进行的;没有交换拍等这样的操作..所以要把所有的不在位的将牌;移动到各自的目标位置上;至少要移动从他们各自的位置到目标位置的距离和这么多次;所以最有路径的耗散值不会比该值小;因此该启发函数hn满足A算法的条件..3、A算法流程图;如图24、A算法总结4.1;把起始状态添加到开启列表..4.2;重复如下工作:a 寻找开启列表中f值最低的节点;我们称它为BESTNOEb 把它切换到关闭列表中..c 对相邻的4个节点中的每一个如果它不在开启列表;也不在关闭列表;把它添加到开启列表中..把BESTNODE作为这一节点的父节点..记录这一节点的f和g值如果它已在开启或关闭列表中;用g值为参考检查新的路径是否更好..更低的g值意味着更好的路径..如果这样;就把这一节点的父节点改为BESTNODE;并且重新计算这一节点的f和g值;如果保持开启列表的f值排序;改变之后需要重新对开启列表排序..d 停止把目标节点添加到关闭列表;这时候路径被找到;或者没有找到路径;开启列表已经空了;这时候路径不存在..4.3;保存路径..从目标节点开始;沿着每一节点的父节点移动直到回到起始节点..这就是求得的路径..5、数据结构采用结构体来保存八数码的状态、f和g的值以及该节点的父节点;struct Node{int s33;//保存八数码状态;0代表空格int f;g;//启发函数中的f和g值struct Node next;struct Node previous;//保存其父节点};6、实验结果;如图3所示图3 A算法求解八数码问题实验结果7、源代码//-----------------------------------------------------------------------------//代码:利用A算法求解八数码问题..//八数码问题的启发函数设计为:fn=dn+pn;其中A算法中的gn根据具体情况设计为dn;意为n节点的深度;而hn设计为pn;意为放错的数码与正确的位置距离之和..//后继结点的获取:数码的移动等效为空格的移动..首先判断空格上下左右的可移动性;其次移动空格获取后继结点..//-----------------------------------------------------------------------------include<stdio.h>include<stdlib.h>include<math.h>//八数码状态对应的节点结构体struct Node{int s33;//保存八数码状态;0代表空格int f;g;//启发函数中的f和g值struct Node next;struct Node previous;//保存其父节点 };int open_N=0; //记录Open列表中节点数目//八数码初始状态int inital_s33={2;8;3;1;6;4;7;0;5};//八数码目标状态int final_s33={1;2;3;8;0;4;7;6;5};//------------------------------------------------------------------------//添加节点函数入口;方法:通过插入排序向指定表添加//------------------------------------------------------------------------void Add_Node struct Node head; struct Node p{struct Node q;ifhead->next//考虑链表为空{ q = head->next;ifp->f < head->next->f{//考虑插入的节点值比链表的第一个节点值小p->next = head->next;head->next = p;}else {whileq->next//考虑插入节点x;形如a<= x <=b{ifq->f < p->f ||q->f == p->f && q->next->f > p->f || q->next->f == p->f{p->next = q->next;q->next = p;break;}q = q->next;}ifq->next == NULL //考虑插入的节点值比链表最后一个元素的值更大q->next = p;}}else head->next = p;}//------------------------------------------------------------------------//删除节点函数入口//------------------------------------------------------------------------void del_Nodestruct Node head; struct Node p{struct Node q;q = head;whileq->next{ifq->next == p{q->next = p->next;p->next = NULL;ifq->next == NULL return;// freep;}q = q->next;}}//------------------------------------------------------------------------//判断两个数组是否相等函数入口//------------------------------------------------------------------------int equalint s133; int s233{int i;j;flag=0;fori=0; i< 3 ; i++forj=0; j< 3 ;j++ifs1ij = s2ij{flag = 1; break;}ifflagreturn 1;else return 0;}//------------------------------------------------------------------------//判断后继节点是否存在于Open或Closed表中函数入口//------------------------------------------------------------------------int exit_Nodestruct Node head;int s33; struct Node Old_Node{struct Node q=head->next;int flag = 0;whileqifequalq->s;s {flag=1;Old_Node->next = q;return 1;}else q = q->next;ifflag return 0;}//-------------------------------------------------------------//计算pn的函数入口//其中pn为放错位的数码与其正确的位置之间距离之和//具体方法:放错位的数码与其正确的位置对应下标差的绝对值之和//------------------------------------------------------------------------int wrong_sumint s33{int i;j;fi;fj;sum=0;fori=0 ; i<3; i++forj=0; j<3; j++{forfi=0; fi<3; fi++forfj=0; fj<3; fj++iffinal_sfifj == sij{sum += fabsi - fi + fabsj - fj;break;}}return sum;}//-------------------------------------------------------------//获取后继结点函数入口//检查空格每种移动的合法性;如果合法则移动空格得到后继结点//------------------------------------------------------------------------int get_successorstruct Node BESTNODE; int direction; struct Node Successor//扩展BESTNODE;产生其后继结点SUCCESSOR{int i;j;i_0;j_0;temp;fori=0; i<3; i++forj=0; j<3; j++Successor->sij = BESTNODE->sij;//获取空格所在位置fori=0; i<3; i++forj=0; j<3; j++ifBESTNODE->sij == 0{i_0 = i; j_0 = j;break;}switchdirection{case 0: ifi_0-1>-1 {temp = Successor->si_0j_0;Successor->si_0j_0 = Successor->si_0-1j_0;Successor->si_0-1j_0 = temp;return 1;}else return 0;case 1: ifj_0-1>-1{temp = Successor->si_0j_0;Successor->si_0j_0 = Successor->si_0j_0-1;Successor->si_0j_0-1 = temp;return 1;}else return 0;case 2: if j_0+1<3{temp = Successor->si_0j_0;Successor->si_0j_0 = Successor->si_0j_0+1;Successor->si_0j_0+1 = temp;return 1;}else return 0;case 3: ifi_0+1<3 {temp = Successor->si_0j_0;Successor->si_0j_0 = Successor->si_0+1j_0;Successor->si_0+1j_0 = temp;return 1;}else return 0;}}//------------------------------------------------------------------------//从OPen表获取最佳节点函数入口//------------------------------------------------------------------------struct Node get_BESTNODEstruct Node Open{return Open->next;}//------------------------------------------------------------------------//输出最佳路径函数入口//------------------------------------------------------------------------void print_Pathstruct Node head{struct Node q; q1;p;int i;j;count=1;p = struct Node mallocsizeofstruct Node;//通过头插法变更节点输出次序p->previous = NULL;q = head;whileq{q1 = q->previous;q->previous = p->previous;p->previous = q;q = q1;}q = p->previous;whileq{ifq == p->previousprintf"八数码的初始状态:\n";else ifq->previous == NULLprintf"八数码的目标状态:\n"; else printf"八数码的中间态%d\n";count++;fori=0; i<3; i++forj=0; j<3; j++{printf"%4d";q->sij;ifj == 2printf"\n";}printf"f=%d; g=%d\n\n";q->f;q->g;q = q->previous;}}//------------------------------------------------------------------------//A子算法入口:处理后继结点//------------------------------------------------------------------------void sub_A_algorithmstruct Node Open; struct Node BESTNODE; struct Node Closed;struct Node Successor{struct Node Old_Node = struct Node mallocsizeofstruct Node;Successor->previous = BESTNODE;//建立从successor返回BESTNODE 的指针Successor->g = BESTNODE->g + 1;//计算后继结点的g值//检查后继结点是否已存在于Open和Closed表中;如果存在:该节点记为old_Node;比较后继结点的g值和表中old_Node节点//g值;前者小代表新的路径比老路径更好;将Old_Node的父节点改为BESTNODE;并修改其f;g值;后者小则什么也不做..//即不存在Open也不存在Closed表则将其加入OPen表;并计算其f值if exit_NodeOpen; Successor->s; Old_Node {ifSuccessor->g < Old_Node->g{Old_Node->next->previous = BESTNODE;//将Old_Node的父节点改为BESTNODEOld_Node->next->g = Successor->g;//修改g值Old_Node->next->f = Old_Node->g + wrong_sumOld_Node->s;//修改f值//排序~~~~~~~~~~~~~~~~~~del_NodeOpen; Old_Node;Add_NodeOpen; Old_Node;}}else if exit_NodeClosed; Successor->s; Old_Node{ifSuccessor->g < Old_Node->g{Old_Node->next->previous = BESTNODE;Old_Node->next->g = Successor->g;Old_Node->next->f = Old_Node->g + wrong_sumOld_Node->s;//排序~~~~~~~~~~~~~~~~~~del_NodeClosed; Old_Node;Add_NodeClosed; Old_Node;}}else {Successor->f = Successor->g + wrong_sumSuccessor->s;Add_NodeOpen; Successor;open_N++;}}//------------------------------------------------------------------------//A算法入口//八数码问题的启发函数为:fn=dn+pn//其中A算法中的gn根据具体情况设计为dn;意为n节点的深度;而hn设计为pn;//意为放错的数码与正确的位置距离之和//------------------------------------------------------------------------void A_algorithmstruct Node Open; struct Node Closed //A算法{int i;j;struct Node BESTNODE; inital; Successor;inital = struct Node mallocsizeofstruct Node;//初始化起始节点fori=0; i<3; i++forj=0; j<3; j++inital->sij = inital_sij;inital->f = wrong_suminital_s;inital->g = 0;inital->previous = NULL;inital->next = NULL;Add_NodeOpen; inital;//把初始节点放入OPEN表open_N++;while1{ifopen_N == 0{printf"failure"; return;}else {BESTNODE = get_BESTNODEOpen;//从OPEN表获取f值最小的BESTNODE;将其从OPEN表删除并加入CLOSED表中del_NodeOpen; BESTNODE;open_N--;Add_NodeClosed; BESTNODE;ifequalBESTNODE->s; final_s {//判断BESTNODE是否为目标节点printf"success\n";print_PathBESTNODE;return;}//针对八数码问题;后继结点Successor的扩展方法:空格二维数组中的0上下左右移动;//判断每种移动的有效性;有效则转向A子算法处理后继节点;否则进行下一种移动else{Successor = struct Node mallocsizeofstruct Node; Successor->next = NULL;ifget_successorBESTNODE; 0; Successorsub_A_algorithm Open; BESTNODE; Closed; Successor;Successor = struct Node mallocsizeofstruct Node; Successor->next = NULL;ifget_successorBESTNODE; 1; Successorsub_A_algorithm Open; BESTNODE; Closed; Successor;Successor = struct Node mallocsizeofstruct Node; Successor->next = NULL;ifget_successorBESTNODE; 2; Successorsub_A_algorithm Open; BESTNODE; Closed; Successor;Successor = struct Node mallocsizeofstruct Node; Successor->next = NULL;ifget_successorBESTNODE; 3; Successorsub_A_algorithm Open; BESTNODE; Closed; Successor;}}}}//------------------------------------------------------------------------//main函数入口//定义Open和Closed列表..Open列表:保存待检查节点..Closed列表:保存不需要再检查的节点//------------------------------------------------------------------------void main{struct Node Open = struct Node mallocsizeofstruct Node;struct Node Closed = struct Node mallocsizeofstruct Node;Open->next = NULL ; Open->previous = NULL;Closed->next =NULL; Closed->previous = NULL;A_algorithmOpen; Closed;}三、实验体会通过这次实验;使我对启发式搜索算法有了更进一步的理解;特别是估计函数hn所起到的巨大重用..一个好的估计函数对于启发式搜索算法来说是十分关键的..。
基于启发式搜索算法A星解决八数码问题

int statue[size][size]; //记录当前节点的状态 struct Node * Tparent; //用来构成搜索树,该树由搜索图的反向指针构成 struct Node * opennext; //用来构成 open 表,该指针指向该节点在 open 表中的下一个 节点 struct Node * closenext; //用来构成 open 表,该指针指向该节点在 close 表中的下一个 节点 struct Node * brothernext; //构成兄弟链表,该指针指向该节点在兄弟链表中的下一个节 点 int f; //记录当前节点的 f 函数值 int g; //记录当前节点的 g 函数的值 int h; //记录当前节点的 h 函数的值 };
5
get_bestroute (bestNode); return; }
2.2.7 生成 bestNode 所指节点的后继节点
定义一个后继节点链表,表头为 head_b,将 bestNode 所指节点的不是前驱节点的后继 节点,链接到后继及诶单链表中。getchild 函数可以实现这个功能。
//产生 bestNode 的一切后继节点。。 head head_b; //定义 bestNode 的后继节点表 head_b.next=NULL; getchild (&head_b,bestNode); //产生 bestNode 的子节点,将不是 bestNode 的父节点的
while (head_b.next!=NULL) { Node *tmp=getbrother (&head_b); //从后继节点表中取出一个节点记为 tmp,并从
八数码 人工智能实验报告

八数码人工智能实验报告八数码人工智能实验报告引言:八数码是一种经典的数学问题,也是人工智能领域中常用的实验题目之一。
本次实验旨在通过使用搜索算法解决八数码问题,探讨人工智能在解决复杂问题上的应用。
一、问题描述:八数码问题是一种数字排列游戏,使用一个3x3的方格,其中8个方格上各有一个数字,剩下一个方格为空白。
通过移动数字方格,最终将数字按照从小到大的顺序排列,空白方格位于最后一个位置。
例如,初始状态为:1 2 38 47 6 5目标状态为:1 2 34 5 67 8二、算法选择:本次实验采用了A*搜索算法来解决八数码问题。
A*算法是一种启发式搜索算法,通过估计每个搜索节点到达目标状态的代价来进行搜索。
它综合了广度优先搜索和最佳优先搜索的优点,能够高效地找到最优解。
三、实验过程:1. 状态表示:在实验中,我们使用一个3x3的二维数组来表示八数码的状态。
数组中的每个元素代表一个方格的数字,空白方格用0表示。
2. 启发函数:为了评估每个搜索节点到达目标状态的代价,我们需要定义一个启发函数。
本实验中,我们选择了曼哈顿距离作为启发函数。
曼哈顿距离是指每个数字方格与其目标位置之间的水平和垂直距离之和。
3. A*算法:A*算法的核心思想是维护一个优先队列,根据每个搜索节点的估价函数值进行排序。
具体步骤如下:- 将初始状态加入优先队列,并设置初始估价函数值为0。
- 从优先队列中取出估价函数值最小的节点,进行扩展。
- 对于每个扩展节点,计算其估价函数值,并将其加入优先队列。
- 重复上述步骤,直到找到目标状态或者队列为空。
四、实验结果:经过实验,我们发现A*算法能够高效地解决八数码问题。
对于初始状态为随机排列的八数码,A*算法能够在较短的时间内找到最优解。
实验结果表明,A*算法在解决八数码问题上具有较好的性能。
五、实验总结:本次实验通过使用A*搜索算法解决八数码问题,展示了人工智能在解决复杂问题上的应用。
A*算法通过合理的启发函数和优先队列的维护,能够高效地找到最优解。
a算法求解八数码问题 实验报告

题目: a算法求解八数码问题实验报告目录1. 实验目的2. 实验设计3. 实验过程4. 实验结果5. 实验分析6. 实验总结1. 实验目的本实验旨在通过实验验证a算法在求解八数码问题时的效果,并对其进行分析和总结。
2. 实验设计a算法是一种启发式搜索算法,主要用于在图形搜索和有向图中找到最短路径。
在本实验中,我们将使用a算法来解决八数码问题,即在3x3的九宫格中,给定一个初始状态和一个目标状态,通过移动数字的方式将初始状态转变为目标状态。
具体的实验设计如下:1) 实验工具:我们将使用编程语言来实现a算法,并结合九宫格的数据结构来解决八数码问题。
2) 实验流程:我们将设计一个初始状态和一个目标状态,然后通过a 算法来求解初始状态到目标状态的最短路径。
在求解的过程中,我们将记录下每一步的状态变化和移动路径。
3. 实验过程我们在编程语言中实现了a算法,并用于求解八数码问题。
具体的实验过程如下:1) 初始状态和目标状态的设计:我们设计了一个初始状态和一个目标状态,分别为:初始状态:1 2 34 5 67 8 0目标状态:1 2 38 0 42) a算法求解:我们通过a算法来求解初始状态到目标状态的最短路径,并记录下每一步的状态变化和移动路径。
3) 实验结果在实验中,我们成功求解出了初始状态到目标状态的最短路径,并记录下了每一步的状态变化和移动路径。
具体的实验结果如下:初始状态:1 2 34 5 67 8 0目标状态:1 2 38 0 47 6 5求解路径:1. 上移1 2 37 8 62. 左移1 2 3 4 0 5 7 8 63. 下移1 2 3 4 8 5 7 0 64. 右移1 2 3 4 8 5 0 7 65. 上移1 2 3 0 8 5 4 7 61 2 38 0 54 7 67. 下移1 2 38 7 54 0 68. 右移1 2 38 7 54 6 0共计8步,成功从初始状态到目标状态的最短路径。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
八数码问题A*算法的实现及性能分析计算机科学与技术学院专业:计算机科学与技术161210404 杨凯迪目录一、8数码问题 (3)1.问题描述 (3)2.八数码问题形式化描述 (3)3.解决方案 (4)二、A*算法 (4)1.A*搜索算法一般介绍 (4)2. A*算法的伪代码 (5)3. 建立合适的启发式 (6)三、算法实现及性能比较 (7)四、算法性能分析 (8)五、结论 (9)六、参考文献 (10)附录 (10)一、8数码问题1.问题描述八数码问题是指这样一种游戏:将分别标有数字1,2,3,…,8 的八块正方形数码牌任意地放在一块3×3 的数码盘上。
放牌时要求不能重叠。
于是,在3×3 的数码盘上出现了一个空格。
现在要求按照每次只能将与空格相邻的数码牌与空格交换的原则,不断移动该空格方块以使其和相邻的方块互换,直至达到所定义的目标状态。
空格方块在中间位置时有上、下、左、右4个方向可移动,在四个角落上有2个方向可移动,在其他位置上有3个方向可移动,问题描述如图1-1所示初始状态过渡状态最终状态图1-1 八数码问题执行过程2.八数码问题形式化描述初始状态:初始状态向量:规定向量中各分量对应的位置,各位置上的数字。
把3×3的棋盘按从左到右,从上到下的顺序写成一个一维向量。
我们可以设定初始状态:<1,5,2,4,0,3,6,7,8>后继函数:按照某种规则移动数字得到的新向量。
例如:<1,5,2,4,0,3,6,7,8> <1,0,2,4,5,3,6,7,8>目标测试:新向量是都是目标状态。
即<1,2,3,4,5,6,7,8,0>是目标状态?路径耗散函数:每次移动代价为1,每执行一条规则后总代价加1。
3.解决方案该问题是一个搜索问题。
它是一种状态到另一种状态的变换。
要解决这个问题,必须先把问题转化为数字描述。
由于八数码是一个3*3的矩阵,但在算法中不实用矩阵,而是将这个矩阵转化为一个一维数组,使用这个一维数组来表示八数码,但是移动时要遵守相关规则。
(1) 可用如下形式的规则来表示数字通过空格进行移动:<a1,a2,a3,a4,a5,a6,a7,a8,a9>→<b1,b2,b3,b4,b5,b6,b7,b8,b9>(2)共24条移动规则,对应与每个位置的移动规则。
(3)搜索顺序举例:1) 优先移动行数小的棋子(数字)2) 同一行中优先移动列数大的棋子(4)约束规则:不使离开既定位置的数字数增加八数码的节点扩展应当遵循棋子的移动规则。
按规则,每一次可以将一个与空格相邻的棋子移动到空格中,实际上也可以看做空格的相反方向移动。
空格的移动方向可以是上下左右,当然不能出边界。
棋子的位置,也就是保存状态的数组元素的下标,空格移动后,相应位置发生变化,在不移出边界的条件下,空格向右,下,左,上移动后,新位置是原位置分别加上1,3,-1,-3。
在这里,空格可以用任意数字表示。
操作本文用u(up) r(right) d(down) l(left) 分别表示空格的向上向右向下向左四个操作。
经分析,8数码问题的搜索策略共有:1.广度优先搜索、2.深度优先搜索、3.有界深度优先搜索、4.最好优先搜索、5.局部择优搜索,等等。
其中广度优先搜索法是可采纳的,有界深度优先搜索法是不完备的,最好优先和局部择优搜索法是启发式搜索法。
本实验采用启发式A*搜索算法来实现。
二、A*算法1.A*搜索算法一般介绍A* 算法实际是一种启发式搜索,所谓启发式搜索,就是利用一个估价函数评估每次的的决策的价值,决定先尝试哪一种方案,这样可以极大的优化普通的广度优先搜索。
一般来说,从出发点(A)到目的地(B)的最短距离是固定的,我们可以写一个函数judge() 估计 A 到 B 的最短距离,如果程序已经尝试着从出发点 A 沿着某条路线移动到了 C 点, 那么我们认为这个方案的 A B 间的估计距离为 A 到 C 实际已经行走了的距离H 加上用judge() 估计出的 C 到B 的距离。
如此,无论我们的程序搜索展开到哪一步,都会算出一个评估值,每一次决策后,将评估值和等待处理的方案一起排序,然后挑出待处理的各个方案中最有可能是最短路线的一部分的方案展开到下一步,一直循环到对象移动到目的地,或所有方案都尝试过却没有找到一条通向目的地的路径则结束。
A*算法是一个可采纳的最好优先算法。
A*算法的估价函数可表示为:f'(n) = g'(n) + h'(n)这里,f'(n)是估价函数,g'(n)是起点到终点的最短路径值,h'(n)是n到目标的最断路经的启发值。
由于这个f'(n)其实是无法预先知道的,所以我们用前面的估价函数f(n)做近似。
g(n)代替g'(n),但g(n)>=g'(n)才可(大多数情况下都是满足的,可以不用考虑),h(n)代替h'(n),但h(n)<=h'(n)才可。
可以证明应用这样的估价函数是可以找到最短路径的,也就是可采纳的。
2. A*算法的伪代码创建两个表,OPEN表保存所有已生成而未考察的节点,CLOSED表中记录已访问过的节点。
算起点的估价值;将起点放入OPEN表;while(OPEN!=NULL){从OPEN表中取估价值f最小的节点n;if(n节点==目标节点){break;}for(当前节点n 的每个子节点X){算X的估价值;if(X in OPEN){if( X的估价值小于OPEN表的X估价值){把n设置为X的父亲;更新OPEN表中的估价值; //取最小路径的估价值}}if(X inCLOSE) {if( X的估价值小于CLOSE表的X估价值){把n设置为X的父亲;将该节点从close表中除去把X节点放入OPEN //取最小路径的估价值}}if(X not inboth){把n设置为X的父亲;求X的估价值;并将X插入OPEN表中; //升序排列open}}//end for将n节点插入CLOSE表中;按照估价值将OPEN表中的节点排序; //实际上是比较OPEN表内节点f 的大小,从最小路径的节点向下进行。
}//end while(OPEN!=NULL)保存路径,即从终点开始,每个节点沿着父节点移动直至起点,这就是你的路径;3. 建立合适的启发式A*算法有个计算公式f(x) = g(x)+h(x)其中g(x)为从起始状态到当前状态所消耗的步数,h(x)为一个启发值,估算从当前状态到目标状态所需的步数,一般h(x)小于等于实际需要步数为好,这样不会将最优解忽略,因为h(x)和解空间有一些关系,如果h(x)设置的比实际需要的步数多,那么解空间就有可能将最优解忽略。
举个例子吧,宽度优先搜索就是h(x)=0带来的效果,深度优先搜索就是g(x)=0带来的效果,不过h(x)距离h*(x)[实际需要的步数]的程度不能过大,否则h(x)就没有过强的区分能力,算法效率并不会很高。
对一个好的h(x)的评价是:h(x)在h*(n)[实际需要的步数]的下界之下,并且尽量接近h*(n)[实际需要的步数].那么8数码问题g(x) 为经过上下左右移动空格附近的数字来得到新状态所需步数,h(x)为当前状态与目标状态的距离,就是所有不在目标位置的数字总和,必然小于h*(x)三、算法实现及性能比较这里通过c++语言来实现各种排序算法(源码见附录),程序运行环境为windows 7,所用编译器为vs2013。
实验分别以不同的初始棋盘和相同的目标棋盘为例进行测试。
初始数码棋盘1:2 0 3 1 4 6 7 5 8初始数码棋盘2:2 4 3 0 6 8 1 7 5初始数码棋盘3:2 3 7 6 4 8 1 0 5初始数码棋盘4:3 2 4 5 0 7 8 1 6初始数码棋盘5:4 0 3 2 6 8 1 7 5初始数码棋盘6:1 4 3 5 7 0 2 6 8初始数码棋盘7:4 6 3 2 8 5 1 0 7目标数码棋盘:1 2 3 4 5 6 7 8 0实验部分结果如图3-1:图3-1.测试结果四、算法性能分析在测试中我们根据不同的初始数码状态相同的目标数码状态,产生不同的移动步骤,并给出了其步数和运行时间(单位ms)。
表1为不同的初始数码状态相同的目标数码状态测试后得到的运行时间数据。
表2为不同的初始数码状态相同的目标数码状态能否的到正确的步骤与否的数据。
表1 步数和运行时间(单位ms )表2 能否得到正确步骤为了直观起见,根据实验数据画出不同的初始数码状态相同的目标数码状态下时间随步数的变化趋势图如图3-2所示:50010001500200025003000T i m e (m s )Steps图3-2时间随步数的变化趋势图根据实验数据表2,我们可得到该算法得到正确步骤路径的概率为:71.42%。
五、结论 最后我们得出结论: 时间性能上,算法所需时间随步数的增加而逐渐呈增加趋势,但并不是线性增长。
部分时间不随移动步数变化。
该算法能得到正确的解概率约为71.42%六、参考文献1.《Artificial intelligence :;a modern approach 人工智能: 一种现代方法》作者:Russell, Stuart J. 出版社:清华大学出版社附录#include<iostream>#include<time.h>#include<windows.h>#include<vector>#include<cmath>using namespace std;struct node{int a[3][3]; //存放矩阵int father; //父节点的位置int gone; //是否遍历过,1为是,0为否int fn; //评价函数的值int x,y; //空格的坐标int deep; //节点深度};vector<node> store; //存放路径节点int mx[4]={-1,0,1,0};int my[4]={0,-1,0,1}; //上下左右移动数组int top; //当前节点在store中的位置bool check(int num) //判断store[num]节点与目标节点是否相同,目标节点储存在store[0]中{for(int i=0;i<3;i++){for(int j=0;j<3;j++){if(store[num].a[i][j]!=store[0].a[i][j])return false;}}return true;}bool search(int num) //判断store[num]节点是否已经扩展过 ,没有扩展返回true {int pre=store[num].father; //pre指向store[num]的父节点位置bool test=true;while(!pre){ //循环直到pre为0,既初始节点for(int i=0;i<3;i++){for (int j=0;j<3;j++){if(store[pre].a[i][j]!=store[num].a[i][j]){test=false;break;}}if(test==false) break;}if(test==true) return false;pre=store[pre].father; //pre继续指向store[pre]父节点位置}return true;}void print(int num) //打印路径,store[num]为目标节点{vector<int> temp; //存放路径int pre=store[num].father;temp.push_back(num);while(pre!=0){ //从目标节点回溯到初始节点temp.push_back(pre);pre=store[pre].father;}cout<<endl;cout<<"*********数码移动步骤*********"<<endl;int mm=1; //步数for(int m=temp.size()-1;m>=0;m--){cout<<"---第"<<mm<<"步---:"<<endl;for(int j=0;j<3;j++){cout<<store[temp[m]].a[i][j]<<" ";}cout<<endl;}mm++;cout<<endl;}cout<<"所需步数为: "<<store[num].deep<<endl;return;}int get_fn(int num) //返回store[num]的评价函数值{int fn_temp=0; //评价函数值bool test=true;for(int i=0;i<3;i++){ //当找到一个值后,计算这个值位置与目标位置的距离差,test置为false后继续寻找下一个值for(int j=0;j<3;j++){test=true;for(int k=0;k<3;k++){for(int l=0;l<3;l++){if((store[num].x!=i||store[num].y!=j)&&store[num].a[i][j]==store[0].a[k][l]){ //寻值时排除空格位fn_temp=fn_temp+abs(i-k)+abs(j-l);test=false;}if(test==false) break;}if(test==false) break;}}}fn_temp=fn_temp+store[num].deep; //加上节点深度return fn_temp;}void kongxy(int num) //获得空格坐标{for(int i=0;i<3;i++){for(int j=0;j<3;j++){if(store[num].a[i][j]==0){store[num].x=i;store[num].y=j;}}return;}int main(){cout<<"-----------A*算法解决8数码问题------------"<<endl;while(true){store.clear(); //清空storevector<int> open; //建立open表int i,j,m,n,f;int min; //store[min]储存fn值最小的节点int temp;bool test;top=1; //当前节点在store的位置,初始节点在store[1]int target[9];int begin[9]; //储存初始状态和目标状态,用于判断奇偶int t1=0,t2=0; //初始状态和目标状态的奇偶序数node node_temp;store.push_back(node_temp);store.push_back(node_temp); //用于创建store[0]和store[1],以便下面使用cout<<"请输入初始数码棋盘状态,0代表空格:"<<endl; //输入初始状态,储存在store[1]中test=false;while(test==false){f=0;for(i=0;i<3;i++){for(j=0;j<3;j++){cin>>temp;store[1].a[i][j]=temp;begin[f++]=temp;}}test=true;for(i=0;i<8;i++){ //检查是否有重复输入,若有则重新输入for(j=i+1;j<9;j++){if(begin[i]==begin[j]){test=false;}}if(test==false) break;}if(test==false) cout<<"输入重复,请重新输入:"<<endl;}kongxy(1); //找出空格的坐标cout<<"请输入目标数码棋盘状态,0代表空格: "<<endl; //输入目标状态,储存在store[0]中test=false;while(test==false){f=0;for(i=0;i<3;i++){for(j=0;j<3;j++){cin>>temp;store[0].a[i][j]=temp;target[f++]=temp;}}test=true;for(i=0;i<8;i++){ //检查是否有重复输入,若有则重新输入for(j=i+1;j<9;j++){if(target[i]==target[j]){test=false;break;}}if(test==false) break;}if(test==false){cout<<"输入重复,请重新输入:"<<endl;continue; //若重复,重新输入}for(i=0;i<9;i++){ //检查目标状态与初始状态是否匹配test=false;for(j=0;j<9;j++){if(begin[i]==target[j]){test=true;break;}if(test==false) break;}if(test==false) cout<<"输入与初始状态不匹配,请重新输入:"<<endl;}for(i=1;i<9;i++){ //判断奇偶序数是否相同,若不相同则无法找到路径for(j=1;i-j>=0;j++){if(begin[i]>begin[i-j]){if(begin[i-j]!=0) t1++;}}}for(i=1;i<9;i++){for(j=1;i-j>=0;j++){if(target[i]>target[i-j]){if(target[i-j]!=0) t2++;}}}if(!(t1%2==t2%2)){cout<<"无法找到路径."<<endl;cout<<endl;//system("pause");//return 0;continue;}LARGE_INTEGER Freg;LARGE_INTEGER Count1,Count2;QueryPerformanceFrequency(&Freg);QueryPerformanceCounter(&Count1);//获取时间Count1double d;store[1].father=0; //初始化参数store[1].gone=0;store[1].deep=0; //初始节点的父节点为0store[1].fn=get_fn(1);if(check(1)){ //判断初始状态与目标状态是否相同print(1);//system("pause");//return 0;cout<<endl;continue;}open.push_back(1); //把初始状态在store中的位置数压入open表中while(!open.empty()){ //当open表不为空时,开始寻找路径if(check(top)) break;min=top;int i_min=0;for(i=0;i<open.size();i++){ //遍历open表中元素,找出store中fn值最小的节点if(store[open[i]].fn<=store[min].fn&&store[open[i]].gone==0){min=open[i];i_min=i;}}store[min].gone=1;open.erase(open.begin()+i_min); //把最小节点标记遍历过,并从open表中删除m=store[min].x;n=store[min].y; //空格坐标for(f=0;f<4;f++){ //上下左右移动空格i=m+mx[f];j=n+my[f];if(i>=0&&i<=2&&j>=0&&j<=2){ //当变换后的空格坐标在矩阵中时,开始移动top++;store.push_back(store[min]); //把store[min]压入store中成为新增节点,位置为store[top]store[top].father=min; //新增节点的父节点为minstore[top].gone=0; //新增节点未被访问store[top].deep=store[min].deep+1; //新增节点的深度为父节点深度+1temp=store[top].a[m][n]; //交换空格与相邻数字store[top].a[m][n]=store[top].a[i][j];store[top].a[i][j]=temp;store[top].x=i; //移动后的空格坐标store[top].y=j;store[top].fn=get_fn(top); //移动后的fn值open.push_back(top); //把top压入open表中if(check(top)){ //检查是否到达目标print(top);//system("pause");//return 0;break;}if(search(top)==false){ //检查新增节点是否被访问过,若访问过,则删除此节点top--;store.pop_back();open.pop_back();}}}}QueryPerformanceCounter(&Count2);//获取时间Count2d=(double)(Count2.QuadPart-Count1.QuadPart)/(double)Freg.QuadPart*1000.0;//计算时间差,d的单位为ms.cout<<"算法时间为为"<<d<<" ms."<<endl;cout<<endl;}return 0;system("pause");}。