启发式搜索 八数码问题
八数码问题启发函数和代价函数
八数码问题启发函数和代价函数八数码问题作为经典的搜索问题,其解决过程中启发函数和代价函数的选择对搜索效率有着重要的影响。
本文将针对八数码问题中启发函数和代价函数的选择进行探讨,并分析它们在搜索过程中的作用和影响。
一、启发函数的选择启发函数是在搜索过程中用来评估节点的“接近程度”的函数,它可以指导搜索算法朝着离目标更近的方向前进,从而提高搜索效率。
在八数码问题中,常用的启发函数有误放置数目、曼哈顿距离和线性冲突等。
1. 误放置数目误放置数目是指当前状态与目标状态中不同数字的个数,它可以作为启发函数来评估当前状态与目标状态的“距离”。
当误放置数目越小,说明当前状态距离目标状态越近,因此误放置数目可以作为一种简单而有效的启发函数。
2. 曼哈顿距离曼哈顿距离是指当前状态的每个数字到目标状态的正确位置之间的曼哈顿距离之和。
曼哈顿距离可以更准确地评估当前状态与目标状态的“距离”,因此在某些情况下,比误放置数目更适合作为启发函数。
3. 线性冲突线性冲突是指在某一行或某一列中有两个数字的目的位置相互交叉,这种情况下移动其中一个数字就会导致另一个数字也需要移动。
线性冲突可以影响搜索的效率,因此考虑线性冲突可以使启发函数更精确地评估当前状态与目标状态的“距离”。
二、代价函数的选择代价函数是指在搜索过程中用来评估节点的“代价”的函数,它可以指导搜索算法在选择候选节点时进行排序,从而提高搜索效率。
在八数码问题中,常用的代价函数有实际代价和估计代价等。
1. 实际代价实际代价是指从初始状态到当前状态的实际代价,它可以作为代价函数来评估当前状态的“代价”。
通过记录从初始状态到当前状态的实际代价,搜索算法可以更准确地评估每个候选节点的“代价”,从而更有针对性地选择下一个节点。
2. 估计代价估计代价是指从当前状态到目标状态的估计代价,它可以作为代价函数来评估当前状态的“代价”。
估计代价通常是通过启发函数来估计的,因此选择合适的启发函数对于估计代价的准确性非常重要。
八数码问题解释
八数码问题解释8数码问题又称9宫问题,源于一个古老的智力游戏。
说白了就是我们小时候玩的“华容道”。
意在给定的9格棋盘的8个格子内分别放一个符号,符号之间互不相同,剩下一格做为“出口”。
我们把8个符号在棋盘上的排列顺序称作8数码的状态,游戏要求给定一个初始的状态与一个终止状态,符号要经过若干次移动后由初态变成终态,这个过程中只有“出口”附近的符号可以朝“出口”的方向移动,且每次只能移动一个符号。
如下图所示,(其中我们用0表示出口,=》表示移动一次,=》*表示移动0-n次):初态终态1 2 3 1 2 3 0 1 24 5 6 =》 4 5 6 =》* 3 4 57 8 0 7 0 8 6 7 82 解决方案通过观察我们可以发现每一次8数码的状态都可以通过移动字符变成有限的几种其他状态,比如上图中我们可以知道初态“出口”附近有8和6可以移动,那么这个初态可以经过移动得到两个新的状态。
我们人在玩这个游戏的时候,总是要做下面几个步骤:1.看看哪个符号可以移动。
2.判断一下哪个符号的移动最有利于到达终态。
3.选定一个符号并移动它。
4.判断是否到达终态,是则结束,否则就回到第一步。
而现在我们要使用机器来模拟这一过程,其步骤与人类类似,但不同的是,人在执行第二部的时候总是能预先判断未来好几步的局势,从而选出最有利的一步,而机器则不行,它要先得到一个状态才能知道这个状态下一步将会到哪些状态而无法像我们一样一次就看到后面几步的状态。
那么基本思想就是让机器穷尽由初态出发到达所有可能状态的路径,并从中找到有终态的路径作为问题的解。
2.1 A*算法就如我们上面说到的让机器找出所有的可能来得到问题的解,看起来似乎很简单,但问题在于一旦8数问题的解达到一定规模,机器所要穷尽的路径数量将变得极为庞大,无疑会消耗大量的时间和空间。
那么如何让机器像人一样在选择移动符号的时候总是能选择最有利的那一个呢?下面就要介绍启发式搜索中的一个算法A*算法来解决这个问题。
启发式搜索浅谈,解决八数码问题
启发式搜索浅谈,解决⼋数码问题博客迁移⾄相信很多⼈都接触过九宫格问题,也就是⼋数码问题。
问题描述如下:在3×3的棋盘,摆有⼋个棋⼦,每个棋⼦上标有1⾄8的某⼀数字,不同棋⼦上标的数字不相同。
棋盘上还有⼀个空格,与空格相邻的棋⼦可以移到空格中。
要求解决的问题是:给出⼀个初始状态和⼀个⽬标状态,找出⼀种从初始转变成⽬标状态的移动棋⼦步数最少的移动步骤。
其实在很早之前我就做过这道题了,当时我⽤的是双向⼴搜,后来⼜⼀知半解的模仿了⼀个启发式搜索(A*)。
昨天在图书馆看书的时候,翻阅了⼀下⼈⼯智能的书籍,⼜发现了这个经典的⼋数码问题。
于是便看了下去,渐渐的明⽩了启发式搜索的真正含义,才知道⾃⼰⼏年前模仿的那个代码是什么意思。
在这篇⽂章⾥,我把昨天的所学东西稍稍的记录⼀下,顺便分享给⼤家,如果有什么错误,欢迎各位指出。
平时,我们所使⽤的搜索算法⼤多数都是⼴搜(BFS)和深搜(DFS),其实他们都是盲⽬搜索,相对来说搜索的状态空间⽐较⼤,效率⽐较低。
⽽启发式搜索则相对的智能⼀些,它能对所有当前待扩展状态进⾏评估,选出⼀个最好的状态、最容易出解的状态进⾏搜索。
这样,我们就可以避免扩展⼤量的⽆效状态,从⽽提⾼搜索效率。
在对状态进⾏评估的时候,我们会使⽤到⼀个这样的等式f(n)=g(n)+h(n)。
那这个等式是什么含义呢?其中g(n)代表到达代价,即从初始状态扩展到状态n的代价值。
h(n)代表状态n的估计出解代价,即从状态n扩展到⽬标状态的代价估计值。
所以,f(n)代表估计整体代价,即⼀个搜索路径上经过状态n且成功出解的估计代价值。
于是,在启发式搜索算法中,我们只要对每⼀个状态,求出其f(n),每次取f(n)最⼩的状态进⾏扩展即可。
那现在还有⼀个问题,就是如何确定g(n)和h(n)到底是什么函数?否则f(n)也⽆法求出。
其实g(n)相对来说⽐较好确定,因为在到达状态n之后,必定有⼀条从初始状态到n的搜索路径,于是我们可以从这条路径上找出到达代价g(n),⼀般的我们就取路径长度即可。
人工智能实验一_八数码问题
用A*算法解决八数码问题1 问题描述1.1 待解决问题的解释八数码游戏(八数码问题)描述为:在3×3组成的九宫格棋盘上,摆有八个将牌,每一个将牌都刻有1-8八个数码中的某一个数码。
棋盘中留有一个空格,允许其周围的某一个将牌向空格移动,这样通过移动将牌就可以不断改变将牌的布局。
这种游戏求解的问题是:给定一种初始的将牌布局或结构(称初始状态)和一个目标的布局(称目标状态),问如何移动将牌,实现从初始状态到目标状态的转变。
1.2 问题的搜索形式描述(4要素)初始状态:8个数字将牌和空格在九宫格棋盘上的所有格局组成了问题的状态空间。
其中,状态空间中的任一种状态都可以作为初始状态。
后继函数:通过移动空格(上、下、左、右)和周围的任一棋子一次,到达新的合法状态。
目标测试:比较当前状态和目标状态的格局是否一致。
路径消耗:每一步的耗散值为1,因此整个路径的耗散值是从起始状态到目标状态的棋子移动的总步数。
1.3 解决方案介绍(原理)对于八数码问题的解决,首先要考虑是否有答案。
每一个状态可认为是一个1×9的矩阵,问题即通过矩阵的变换,是否可以变换为目标状态对应的矩阵?由数学知识可知,可计算这两个有序数列的逆序值,如果两者都是偶数或奇数,则可通过变换到达,否则,这两个状态不可达。
这样,就可以在具体解决问题之前判断出问题是否可解,从而可以避免不必要的搜索。
如果初始状态可以到达目标状态,那么采取什么样的方法呢?常用的状态空间搜索有深度优先和广度优先。
广度优先是从初始状态一层一层向下找,直到找到目标为止。
深度优先是按照一定的顺序前查找完一个分支,再查找另一个分支,以至找到目标为止。
广度和深度优先搜索有一个很大的缺陷就是他们都是在一个给定的状态空间中穷举。
这在状态空间不大的情况下是很合适的算法,可是当状态空间十分大,且不预测的情况下就不可取了。
他的效率实在太低,甚至不可完成。
由于八数码问题状态空间共有9!个状态,对于八数码问题如果选定了初始状态和目标状态,有9!/2个状态要搜索,考虑到时间和空间的限制,在这里采用A*算法作为搜索策略。
人工智能实验报告,包括八数码问题八皇后问题和tsp问题
八数码问题(一)问题描述在一个3*3的方棋盘上放置着1,2,3,4,5,6,7,8八个数码,每个数码占一格,且有一个空格。
这些数码可以在棋盘上移动,其移动规则是:与空格相邻的数码方格可以移入空格。
现在的问题是:对于指定的初始棋局和目标棋局,给出数码的移动序列。
该问题称八数码难题或者重排九宫问题。
(二)问题分析八数码问题是个典型的状态图搜索问题。
搜索方式有两种基本的方式,即树式搜索和线式搜索。
搜索策略大体有盲目搜索和启发式搜索两大类。
盲目搜索就是无“向导”的搜索,启发式搜索就是有“向导”的搜索。
1、启发式搜索由于时间和空间资源的限制,穷举法只能解决一些状态空间很小的简单问题,而对于那些大状态空间的问题,穷举法就不能胜任,往往会导致“组合爆炸”。
所以引入启发式搜索策略。
启发式搜索就是利用启发性信息进行制导的搜索。
它有利于快速找到问题的解。
由八数码问题的部分状态图可以看出,从初始节点开始,在通向目标节点的路径上,各节点的数码格局同目标节点相比较,其数码不同的位置个数在逐渐减少,最后为零。
所以,这个数码不同的位置个数便是标志一个节点到目标节点距离远近的一个启发性信息,利用这个信息就可以指导搜索。
即可以利用启发信息来扩展节点的选择,减少搜索范围,提高搜索速度。
启发函数设定。
对于八数码问题,可以利用棋局差距作为一个度量。
搜索过程中,差距会逐渐减少,最终为零,为零即搜索完成,得到目标棋局。
(三)数据结构与算法设计该搜索为一个搜索树。
为了简化问题,搜索树节点设计如下:struct Chess//棋盘{int cell[N][N];//数码数组int Value;//评估值Direction BelockDirec;//所屏蔽方向struct Chess * Parent;//父节点};int cell[N][N]; 数码数组:记录棋局数码摆放状态。
int Value; 评估值:记录与目标棋局差距的度量值。
Direction BelockDirec; 所屏蔽方向:一个屏蔽方向,防止回推。
八数码问题
人工智能实验报告一.八数码问题及分析:八数码问题也称为九宫问题。
在3×3的棋盘,摆有八个数码,分别为1至8的某一数字,数字各不相同。
棋盘上还有一个空格,与空格相邻的数码可以移到空格中。
要求解决的问题是:给出一个初始状态和一个目标状态,找出一种从初始转变成目标状态的移动数码步数最少的移动步骤。
其中,问题的一个状态就是数码在棋盘上的一种摆法。
数码移动后,状态就会发生改变。
解八数码问题实际上就是找出从初始状态到达目标状态所经过的一系列中间过渡状态。
八数码问题一般使用搜索法来解。
搜索法有广度优先搜索法、深度优先搜索法、A*算法等。
本次试验采用局部择优A算法(即启发函数的有界深度搜索算法)。
二.实验说明:因为在学生社团工作,中间一段时间一直不能够和大家一起准备本次实验,也不好意思拖延大家的进度,于是,我选在工作之后自己一个人做本次试验,但因为技术方面的不够成熟,实际上我查找了许多资料,同时向学长请教用C++builder实现了局部最优的算法。
在算法分析中主要比较讨论了广度优先算法和A*算法,也是由A*算法得到的灵感利用启发函数,但最终的搜索却选择了有界深度的方式。
这是因为八数码问题相对简单,利用有界能够很好的控制算法的中止。
.软件环境:Borland C++ Builder 6三.数据结构设计1.用二唯数组定义棋子的变换(空格的左移、右移、上移、下移)2.定义权值3.定义指向父亲和孩子的指针。
程序如下(Tlist.h):#ifndef KTListH#define KTListH//节点模板类(如KTNode<int>* Paths; 使用)template <class DATATYPE>class KTNode{public:DATATYPE Data; //可变数据类型的节点数据int Weight; //排序用的权重KTNode* Next; //指向下一个节点的指针KTNode* Prior; //指向上一个节点的指针KTNode(){Weight = 0;}//拷贝构造函数KTNode(KTNode& S){Data=S.Data;Weight=S.Weight;Next=S.Next;Prior=S.Prior;}KTNode operator=(KTNode S) //重载运算符,使两个KTNode对象能相互赋值{Data=S.Data;Weight=S.Weight;Next=S.Next;Prior=S.Prior;return *this; //调用拷贝构造函数}};四.算法分析:I)广度优先搜索法1.广度优先搜索算法的基本步骤1)建立一个队列,将初始结点入队,并设置队列头和尾指针2)取出队列头(头指针所指)的结点进行扩展,从它扩展出子结点,并将这些结点按扩展的顺序加入队列。
(完整版)启发式搜索算法
人工智能基础实验报告实验名称:八数码问题姓名:张俊学号:2220092333指导老师:邓安生启发式搜索算法1. 实验内容:使用启发式搜索算法求解8数码问题。
⑴ 编制程序实现求解8数码问题A *算法,采用估价函数()()()()w n f n d n p n ⎧⎪=+⎨⎪⎩, 其中:()d n 是搜索树中结点n 的深度;()w n 为结点n 的数据库中错放的棋子个数;()p n 为结点n 的数据库中每个棋子与其目标位置之间的距离总和。
⑵ 分析上述⑴中两种估价函数求解8数码问题的效率差别,给出一个是()p n 的上界的()h n 的定义,并测试使用该估价函数是否使算法失去可采纳性。
2. 实验目的熟练掌握启发式搜索A *算法及其可采纳性。
3. 实验原理八数码问题是在3行和3列构成的九宫棋盘上放置数码为1到8的8个棋盘,剩下一个空格的移动来不断改变棋盘的布局,求解这类问题的方法是:给定初始布局(即初始状态)和目标布局(即目标状态),定义操作算子的直观方法是为每个棋牌制定一套可能的走步》上,下,左,右四种移动,再根据所定义的启发式搜索函数在搜索过程中选择最合适的操作算子,得到最优的路径。
4.源代码#include <iomanip>#include <stdlib.h>#include <time.h>#include <iostream>#include <stdio.h>#include <conio.h>#include <math.h>//以上为C++源文件using namespace std;static int space=0;int target[9];class EightNum//定义一个EightNum 类{public:int num[9];int f;//初始状态与目标状态相比,棋子错放个数int deap;//深度int evalfun;//状态的估价值EightNum *parent;//以下为类内成员函数的声明EightNum(int nnum[9]);int get_evalfun();int get_deapfun();void eval_func(int id);int Canspread(int n);void Spreadchild(int n);void getnum(int num1[9]);void setnum(int num1[9]);void show(void);int operator ==(EightNum& NewEightN);int operator ==(int num2[9]);int Shownum();};//-----------------------以下为EightNum类成员函数定义-----------------// class Stack{private:EightNum * eightnum;public:Stack * next;EightNum * Minf();EightNum * Belong(EightNum * suc);void Putinto(EightNum * suc);};EightNum::EightNum(int nnum[9]){//此函数功能为:初始化num[];for(int i=0;i<9;i++)num[i]=nnum[i];f=0;deap=0;parent=NULL;}int EightNum::get_evalfun(){return evalfun;}int EightNum::get_deapfun(){return deap;}void EightNum::eval_func(int id){//此函数为估价函数int i,qifa;qifa=0;switch(id){case 1:{for(i=0;i<9;i++){if(num[i]!=target[i])qifa++;}break;}case 2:{int j, h1,h2;for(i=0;i<9;i++){for(j=0;j<9;j++){if(num[j]==i)h1=j;if(target[j]==i)h2=j;}qifa+=(int)(fabs((double)(h1/3 - h2/3)) + fabs((double)(h1%3 - h2%3)));}break;}case 3:{int j, h1,h2;for(i=0;i<9;i++){for(j=0;j<9;j++){if(num[j]==i)h1=j;if(target[j]==i)h2=j;}qifa+=(int)(fabs((double)(h1/3 - h2/3)) + fabs((double)(h1%3 - h2%3)));}qifa=3*qifa;break;}default :break;}f=qifa;if(this->parent==NULL) deap=0;else deap=this->parent->deap+1;evalfun=deap+f;}int EightNum::Canspread(int n){//判断空格"0"可否移动int i,flag = 0;for(i = 0;i < 9;i++)if(this->num[i] == 0)break;switch(n){case 1:if(i/3 != 0)flag = 1;break;case 2:if(i/3 != 2)flag = 1;break;case 3:if(i%3 != 0)flag = 1;break;case 4:if(i%3 != 2)flag = 1;break;default:break;}return flag ;}void EightNum::Spreadchild(int n){//扩展child节点的子节点int i,loc,qifa;for(i = 0;i < 9;i++)this->num[i] = this->parent->num[i];for(i = 0;i < 9;i++)if(this->num[i] == 0)break;if(n==0)loc = i%3+(i/3 - 1)*3;else if(n==1)loc = i%3+(i/3 + 1)*3;else if(n==2)loc = i%3-1+(i/3)*3;elseloc = i%3+1+(i/3)*3;qifa = this->num[loc];this->num[i] = qifa;this->num[loc] = 0;}void EightNum::getnum(int num1[9]){ for(int i=0;i<9;i++)num1[i]=num[i];}void EightNum::setnum(int num1[9]){ for(int i=0;i<9;i++)num[i]=num1[i];}void EightNum::show(){//输出函数for(int i=0;i<9;i++){cout<<num[i]<<" ";if((i+1)%3==0)cout<<"\n";}cout<<"--------------------";}int EightNum::Shownum(){if(this == NULL)return 0;else{int n = this->parent->Shownum();this->show();cout<<endl;return n+1;}}int EightNum::operator ==(EightNum& NewEightN){int compere=1;for(int i=0;i<9;i++)if(num[i]!=NewEightN.num[i]){compere=0;break;}if(compere==0) return 0;else return 1;}//-----------------------以下为分函数的定义---------------------////判断是否有解的函数int solve(int num[9],int target[9]){int i,j;int num_con=0,tar_con=0;for(i=0;i<9;i++)for(j=0;j<i;j++){if(num[j]<num[i] && num[j]!=0)num_con++;if(target[j]<target[i] && target[j]!=0)tar_con++;}num_con=num_con%2;tar_con=tar_con%2;if((num_con==0 && tar_con==0)||(num_con==1 && tar_con==1))return 1;elsereturn 0;}EightNum * Stack::Minf(){Stack * qifa =this->next;Stack * min = this->next;Stack * minp = this;EightNum * minx;while(qifa->next != NULL){if((qifa->next->eightnum->get_evalfun()) < (min->eightnum->get_evalfun())){min = qifa->next;minp = qifa;}qifa = qifa->next;}minx = min->eightnum;qifa = minp->next;minp->next = minp->next->next;free(qifa);return minx;}//判断节点是否属于OPEN表或CLOSED表EightNum * Stack::Belong(EightNum * suc){Stack * qifa = this-> next ;if(qifa == NULL)return NULL;while(qifa != NULL){if(suc==qifa->eightnum)return qifa ->eightnum;qifa = qifa->next;}return NULL;}//把节点存入OPEN 或CLOSED 表中void Stack::Putinto(EightNum * suc){Stack * qifa;qifa =(Stack *) malloc(sizeof(Stack));qifa->eightnum = suc;qifa->next = this->next;this->next = qifa;}int BelongProgram(EightNum * suc ,Stack *Open ,Stack *Closed ,EightNum goal,int m ){EightNum * qifa = NULL;int flag = 0;if((Open->Belong(suc) != NULL) || (Closed->Belong(suc) != NULL)){if(Open->Belong(suc) != NULL) qifa = Open->Belong(suc);else qifa = Closed->Belong(suc);flag=1;}else{Open->Putinto(suc);suc->eval_func(m);}return flag;}//扩展后继节点总函数void Spread(EightNum * suc, Stack * Open, Stack * Closed, EightNum goal,int m){int i;EightNum * child;for(i = 0; i < 4; i++){if(suc->Canspread(i+1)){space++;child = (EightNum *) malloc(sizeof(EightNum));child->parent = suc;child->Spreadchild(i);child->eval_func(m);if(BelongProgram(child, Open, Closed, goal,m)) //判断子节点是否属于OPEN或CLOSED表free(child);}}}//执行函数EightNum * Process(EightNum * org, EightNum goal, Stack * Open, Stack * Closed,int m){while(1){if(Open->next == NULL)return NULL;EightNum * minf =Open->Minf();Closed->Putinto(minf);if((*minf)==goal)return minf;Spread(minf, Open, Closed, goal,m);}}//------------------------A*算法搜索函数----------------------//void A(int id,EightNum start,EightNum Target){EightNum * result;space=0;float time;Stack *Open = (Stack *) malloc(sizeof(Stack));Open->next = NULL;Stack *Closed = (Stack *) malloc(sizeof(Stack));Closed->next = NULL;clock_t startt,finisht;startt=clock();//开始时间start.eval_func(id);Open->Putinto(&start);result = Process(&start, Target, Open, Closed,id); //进行剩余的操作cout<<"\n搜索过程:\n"<<result->Shownum()<<endl;finisht=clock();time=(float)(finisht-startt);cout<<endl<<id<<"算法处理结果:所耗时间:";cout<<time;cout<<"ms, ";cout<<"所耗空间:";cout<<space;cout<<"块, "<<endl<<endl;}//-----------------------------主函数-----------------------------//int main(void)//主函数{int i,j;int flag;int num[9];int error;do{error=0;cout<<"请输入八数码问题的初始状态(0代表空格,“棋子”间用空格隔开):"<<endl;for(i=0;i<9;i++){flag=0;cin>>num[i];for(j=0;j<i;j++)if(num[j]==num[i])flag=1;if(num[i]<0||num[i]>8||flag==1){error++;}}if(error!=0)cout<<"输入数据错误!请重新输入!"<<endl;}while(error!=0);//输入八数码问题的初始状态(0代表空格,“棋子”间用空格隔开);int error1;do{error1=0;cout<<"请输入新的目标状态(用0代表空格,“棋子”间用空格隔开):"<<endl;for(i=0;i<9;i++){flag=0;cin>>target[i];for(j=0;j<i;j++)if(target[j]==target[i])flag=1;if(target[i]<0||target[i]>9||flag==1){error1++;}}if(error1!=0)cout<<"输入数据错误!请重新输入!"<<endl;}while(error1!=0);//输入八数码问题的目标状态(用0代表空格,中间用空格隔开);EightNum start(num),Target(target);int m=solve(num,target);//判断初始状态到目标状态是否有解,有解返回1,误解返回0;if(m==0){cout<<"此状态无解!"<<endl;return 0;}int id=0;while(id!=3){cout<<"1. 错放的棋子个数为;\n2.每个棋子与目标位置之间的距离总和为;"<<endl;cout<<"3.结束,退出程序!"<<endl;cout<<"\n请选择功能,分别输入“1”“2”“3”进行选择:"<<endl;cin>>id;switch(id){case 1:{cout<<"错放的棋子个数结果为:\n(以下逐一展示搜索过程:)"<<endl;A(1,start,Target);break;}case 2:{cout<<"每个棋子与其目标位置之间的距离总和为:\n(以下逐一展示搜索过程:)"<<endl;A(2,start,Target);break;}default: break;}}cout<<"啊啊….程序结束!!";}实验截图实验中遇到的问题1:开始程序只能运行一种方式即按照错位个数搜索,后经过查找相关资料,修改后可程序可进行选择,两种方法结合在一起根据选择运行。
启发式搜索-八数码问题
启发式搜索1. 介绍八数码问题也称为九宫问题。
在3×3的棋盘,摆有八个棋子,每个棋子上标有1至8的某一数字,不同棋子上标的数字不相同。
棋盘上还有一个空格〔以数字0来表示〕,与空格相邻的棋子可以移到空格中。
要求解决的问题是:给出一个初始状态和一个目标状态,找出一种从初始转变成目标状态的移动棋子步数最少的移动步骤。
所谓问题的一个状态就是棋子在棋盘上的一种摆法。
解八数码问题实际上就是找出从初始状态到达目标状态所经过的一系列中间过渡状态。
2. 使用启发式搜索算法求解8数码问题。
1) A ,A 星算法采用估价函数()()()()w n f n d n p n ⎧⎪=+⎨⎪⎩, 其中:()d n 是搜索树中结点n 的深度;()w n 为结点n 的数据库中错放的棋子个数;()p n 为结点n 的数据库中每个棋子与其目标位置之间的距离总和。
2)宽度搜索采用f(i)为i 的深度,深度搜索采用f(i)为i 的深度的倒数。
3. 算法流程① 把起始节点S 放到OPEN 表中,并计算节点S 的)(S f ;② 如果OPEN 是空表,则失败退出,无解;③ 从OPEN 表中选择一个f 值最小的节点i 。
如果有几个节点值相同,当其中有一个 为目标节点时,则选择此目标节点;否则就选择其中任一个节点作为节点i ; ④ 把节点i 从 OPEN 表中移出,并把它放入 CLOSED 的已扩展节点表中; ⑤ 如果i 是个目标节点,则成功退出,求得一个解;⑥ 扩展节点i ,生成其全部后继节点。
对于i 的每一个后继节点j :计算)(j f ;如果j 既不在OPEN 表中,又不在CLOCED 表中,则用估价函数f 把 它添入OPEN 表中。
从j 加一指向其父节点i 的指针,以便一旦找到目标节点时记住一个解答路径;如果j 已在OPEN 表或CLOSED 表中,则比较刚刚对j 计算过的f 和前面计算过的该节点在表中的f 值。
如果新的f 较小,则(I)以此新值取代旧值。
课程设计 用A算法解决8数码问题
课程设计用A算法解决8数码问题
8数码问题是一个组合优化问题,是指给定8个数字,请将这些数字填入九宫格,使
九宫格中每行、列、粗实线和细实线中的数字之和都相等。
本文重点讨论的是用A算法解
决8数码问题的方法,即A算法估价函数。
A算法属于启发式搜索,它的原理是:先计算当前状态的分数,再根据该分数估计状
态所代表的最终的价值,以作为当前局面的启发,判断当前局面是否是最优局面。
针对 8数码问题,A算法估价函数可计算九宫格每行、列和粗实线差值总和,它代表
九宫格中九位之和是如何与其目标值(45)的偏差程度。
根据九宫格中九位之和的偏差程
度定义该九宫格的分数:若九位之和与其目标值相等,则分数成为 0;若差值跨越两个数字,则分数变为 2;若差值跨越一个数字,则分数变为 1。
有了这一定义,A算法便可应
用在8数码问题中了。
正如上述,A算法估价函数的总分数可由九宫格所有表项的偏差程度来定义,若九宫
格所有表项的结果均跟其目标值(45)相等,总分数则为 0,反之则不是,总分数就会根
据表项有多大的偏差程度来决定。
然后A算法搜索遍历到的每个状态都可以根据它对应的
分数计算当前状态的价值,以作为启发,最终定位最优状态。
从理论上讲,A算法可以在求解8数码问题时取得良好的运算的结果,它可以很好的
评估问题的最优解,因此使得搜索树更加有效,从而减少计算机运算时间,提升解答效率。
人工智能-A算法求解8数码问题
实验四 A*算法求解8数码问题一、实验目的熟悉和掌握启发式搜索的定义、估价函数和算法过程,并利用A*算法求解8数码难题,理解求解流程和搜索顺序。
二、实验原理A*算法是一种启发式图搜索算法,其特点在于对估价函数的定义上。
对于一般的启发式图搜索,总是选择估价函数f值最小的节点作为扩展节点。
因此,f 是根据需要找到一条最小代价路径的观点来估算节点的,所以,可考虑每个节点n的估价函数值为两个分量:从起始节点到节点n的实际代价g(n)以及从节点n 到达目标节点的估价代价h(n),且h(n)<=h*(n),h*(n)为n节点到目标节点的最优路径的代价。
八数码问题是在3×3的九宫格棋盘上,排放有8个刻有1~8数码的将牌。
棋盘中有一个空格,允许紧邻空格的某一将牌可以移到空格中,这样通过平移将牌可以将某一将牌布局变换为另一布局。
针对给定的一种初始布局或结构(目标状态),问如何移动将牌,实现从初始状态到目标状态的转变。
如图1所示表示了一个具体的八数码问题求解。
图1 八数码问题的求解三、实验内容1、参考A*算法核心代码,以8数码问题为例实现A*算法的求解程序(编程语言不限),要求设计两种不同的估价函数。
2、在求解8数码问题的A*算法程序中,设置相同的初始状态和目标状态,针对不同的估价函数,求得问题的解,并比较它们对搜索算法性能的影响,包括扩展节点数、生成节点数等。
3、对于8数码问题,设置与图1所示相同的初始状态和目标状态,用宽度优先搜索算法(即令估计代价h(n)=0的A*算法)求得问题的解,记录搜索过程中的扩展节点数、生成节点数。
4、提交实验报告和源程序。
四.实验截图五.源代码#include<iostream>#include"stdio.h"#include"stdlib.h"#include"time.h"#include"string.h"#include<queue>#include<stack>using namespace std;const int N=3;//3*3棋?盘ìconst int Max_Step=32;//最?大洙?搜?索÷深?度èenum Direction{None,Up,Down,Left,Right};//方?向ò,?分?别纄对?应畖上?下?左哩?右?struct Chess//棋?盘ì{int chessNum[N][N];//棋?盘ì数簓码?int Value;//评à估à值μDirection BelockDirec;//所ù屏á蔽?方?向òstruct Chess * Parent;//父?节ú点?};void PrintChess(struct Chess *TheChess);//打洙?印?棋?盘ìstruct Chess * MoveChess(struct Chess * TheChess,Direction Direct,bool CreateNewChess);//移?动ˉ棋?盘ì数簓字?int Appraisal(struct Chess * TheChess,struct Chess * Target);//估à价?函ˉ数簓struct Chess * Search(struct Chess* Begin,struct Chess * Target);//A*搜?索÷函ˉ数簓int main(){//本?程ì序ò的?一?组哩?测a试?数簓据Y为a/*初?始?棋?盘ì*1 4 0**3 5 2**6 7 8**//*目?标括?棋?盘ì*0 1 2**3 4 5**6 7 8**/Chess Target;Chess *Begin,*ChessList;Begin=new Chess;int i;cout<<"请?输?入?初?始?棋?盘ì,?各÷数簓字?用?空?格?隔?开a:阰"<<endl;for(i=0;i<N;i++){for(int j=0;j<N;j++){cin>>Begin->chessNum[i][j];}}cout<<"请?输?入?目?标括?棋?盘ì,?各÷数簓字?用?空?格?隔?开a:阰"<<endl;for(i=0;i<N;i++){for(int j=0;j<N;j++){cin>>Target.chessNum[i][j];}}//获?取?初?始?棋?盘ìAppraisal(Begin,&Target);Begin->Parent=NULL;Begin->BelockDirec=None;Target.Value=0;cout<<"初?始?棋?盘ì:";PrintChess(Begin);cout<<"目?标括?棋?盘ì:";PrintChess(&Target);ChessList=Search(Begin,&Target);//搜?索÷//打洙?印?if(ChessList){/*将?返う?回?的?棋?盘ì列表括?利?用?栈?将?其?倒?叙e*/Chess *p=ChessList;stack<Chess *>Stack;while(p->Parent!=NULL){Stack.push(p);p=p->Parent;}cout<<"搜?索÷结á果?:"<<endl;int num=1;while(!Stack.empty()){cout<<"第台?<<num<<"步?: ";num++;PrintChess(Stack.top());Stack.pop();}cout<<"\n完?成é!"<<endl;}elsecout<<"搜?索÷不?到?结á果?,?搜?索÷深?度è大洙?于?2\n"<<endl;return 0;}//打洙?印?棋?盘ìvoid PrintChess(struct Chess *TheChess){cout<<"(评à估à值μ为a";cout<<TheChess->Value;cout<<")"<<endl;for(int i=0;i<N;i++){cout<<" ";for(int j=0;j<N;j++){cout<<TheChess->chessNum[i][j]<<" ";}cout<<endl;}}//移?动ˉ棋?盘ìstruct Chess * MoveChess(struct Chess * TheChess,Direction Direct,bool CreateNewChess) {struct Chess * NewChess;//获?取?空?闲D格?位?置?int i,j;for(i=0;i<N;i++){bool HasGetBlankCell=false;for(j=0;j<N;j++){if(TheChess->chessNum[i][j]==0){HasGetBlankCell=true;break;}}if(HasGetBlankCell)break;}int ii=i,jj=j;bool AbleMove=true;//判D断?是?否?可é以?移?动ˉswitch(Direct){case Up:i++;if(i>=N)AbleMove=false;break;case Down:i--;if(i<0)AbleMove=false;break;case Left:j++;if(j>=N)AbleMove=false;break;case Right:j--;if(j<0)AbleMove=false;break;};if(!AbleMove)//不?可é以?移?动ˉ则ò返う?回?原-节ú点?{return TheChess;}if(CreateNewChess){NewChess=new Chess();for(int x=0;x<N;x++){for(int y=0;y<N;y++)NewChess->chessNum[x][y]=TheChess->chessNum[x][y];//创洹?建¨新?棋?盘ì,?此?时骸?值μ与?原-棋?盘ì一?致?}}elseNewChess=TheChess;NewChess->chessNum[ii][jj] = NewChess->chessNum[i][j];//移?动ˉ数簓字?NewChess->chessNum[i][j]=0;//将?原-数簓字?位?置?设Θ?置?为a空?格?return NewChess;}//估à价?函ˉ数簓int Appraisal(struct Chess * TheChess,struct Chess * Target){int Value=0;for(int i=0;i<N;i++){for(int j=0;j<N;j++){if(TheChess->chessNum[i][j]!=Target->chessNum[i][j])Value++;}}TheChess->Value=Value;return Value;}//A*搜?索÷函ˉ数簓struct Chess * Search(struct Chess* Begin,struct Chess * Target){Chess *p1,*p2,*p;int Step=0;//深?度èp=NULL;queue<struct Chess *> Queue;Queue.push(Begin);//初?始?棋?盘ì入?队ó//搜?索÷do{p1=(struct Chess *)Queue.front();Queue.pop();//出?队ófor(int i=1;i<=4;i++)//分?别纄从洙?四?个?方?向ò推?导?出?新?子哩?节ú点? {Direction Direct=(Direction)i;if(Direct==p1->BelockDirec)//跳?过y屏á蔽?方?向òcontinue;p2=MoveChess(p1,Direct,true);//移?动ˉ数簓码?if(p2!=p1)//数簓码?是?否?可é以?移?动ˉ{Appraisal(p2,Target);//对?新?节ú点?估à价?if(p2->Value<=p1->Value)//是?否?为a优?越?节ú点?{p2->Parent=p1;switch(Direct)//设Θ?置?屏á蔽?方?向ò,防え?止1往?回?推?{case Up:p2->BelockDirec=Down;break;case Down:p2->BelockDirec=Up;break;case Left:p2->BelockDirec=Right;break;case Right:p2->BelockDirec=Left;break;}Queue.push(p2);//存?储洹?节ú点?到?待鋣处鋦理え?队ó列if(p2->Value==0)//为a0则ò,搜?索÷完?成é{p=p2;i=5;}}else{delete p2;//为a劣ⅷ?质ê节ú点?则ò抛×弃úp2=NULL;}}}Step++;if(Step>Max_Step)return NULL;}while(p==NULL || Queue.size()<=0);return p;}六、实验报告要求1、分析不同的估价函数对A*搜索算法性能的影响等。
八数码问题启发函数和代价函数
八数码问题启发函数和代价函数八数码问题是一种经典的搜索问题,涉及到在一个3x3的方格中,将初始状态和目标状态之间的状态转化成最少步数的问题。
启发函数和代价函数是在解决八数码问题中非常重要的概念。
启发函数(Heuristic Function)又称为估价函数,是用来评估当前状态与目标状态之间的距离的函数。
启发函数通过对当前状态和目标状态的特征进行比较,给出一个估计的代价值,用来指导搜索算法的方向和优先级。
常用的启发函数有曼哈顿距离、欧几里得距离和不在位的数量等。
曼哈顿距离(Manhattan distance)是启发函数中最常用的一种,其计算方式是将当前状态与目标状态中每个数字的位置之差相加。
例如,在八数码中,如果数字5在当前状态中的位置是(1,2),而在目标状态中的位置是(2,2),那么曼哈顿距离就等于|1-2|+|2-2|=1。
通过计算所有数字的曼哈顿距离之和,可以得到当前状态与目标状态的估计代价。
欧几里得距离(Euclidean distance)是另一种常用的启发函数,其计算方式是将当前状态与目标状态中每个数字的位置之差的平方和开根号。
这种启发函数更加关注数字之间的空间距离,而非仅仅是位置之差。
例如,在八数码中,如果数字5在当前状态中的位置是(1,2),而在目标状态中的位置是(2,2),那么欧几里得距离就等于√[(1-2)²+(2-2)²]=1。
通过计算所有数字的欧几里得距离之和,可以得到当前状态与目标状态的估计代价。
不在位的数量(Number of misplaced tiles)是另一种简单直观的启发函数,其计算方式是比较当前状态和目标状态中不同数字的数量。
例如,在八数码中,如果当前状态中的数字已经完全和目标状态一致,那么不在位的数量就为0;如果有一个数字不同,那么不在位的数量就为1。
通过计算所有数字的不在位数量,可以得到当前状态与目标状态的估计代价。
代价函数(Cost Function)是用来衡量当前状态和初始状态之间的距离的函数。
八数码问题
八数码问题
八数码问题是一个经典的问题,也称为滑动谜题。
问题的描述是:在一个3x3的棋盘上,有1-8这8个数字和一个空格组成的九个格子,目标是通过移动数字,将棋盘上的数字按从小到大的顺序排列,空格在最后一个位置。
解决这个问题的算法主要有搜索算法,其中最常见的是A*算法。
A*算法是一种启发式搜索算法,通过建立一个状态空间图,并使用启发式函数估算每个状态到目标状态的距离,来优化搜索过程。
在八数码问题中,启发式函数可以使用曼哈顿距离来估算。
另外,也可以使用深度优先搜索、广度优先搜索、IDA*等搜索算法来解决八数码问题。
这些算法在搜索过程中以不同的方式遍历状态空间图,找到最优解。
解决八数码问题的具体步骤一般如下:
1. 定义初始状态和目标状态。
2. 使用搜索算法进行搜索,找到从初始状态到目标状态的最优解。
3. 在搜索过程中,需要注意状态的合法性和重复状态的处理。
4. 输出最优解,即一系列移动操作,将初始状态转化为目标状态。
需要注意的是,八数码问题可能存在无解的情况,需要在搜索过程中判断并处理无解情况。
此外,由于八数码问题的状态空间较大,搜索过程可能需要一定的时间和空间复杂度。
因此,在实现解决算法时,需要考虑性能优化的问题。
启发式搜索解决八数码问题
#include"stdio.h"int goal[9]={1,2,3,8,0,4,7,6,5},sgoal[9];//goal为棋盘的目标布局,并用中间状态sgoal与之比较struct Board{int pos[9];int d,f,e;//d:深度;f:启发函数;e:记录前一次的扩展节点};struct NodeLink{Board boardstate;NodeLink *parent;NodeLink *previous;NodeLink *next;NodeLink *path;};//更新纪录八数码的状态void setboard(int a[],int b[],int flag) //flag=0,写棋子;flag=1,写棋盘{for(int i=0;i<=8;i++)if(flag)a[b[i]]=i;elseb[a[i]]=i;}//计算启发值的函数int calvalue(int a[]) //不在位棋子数{int c=0;for(int i=0;i<=8;i++)if(a[i]!=goal[i])if(goal[i]!=0)c++;return c;}//生成一个新节点的函数NodeLink *makenode(NodeLink *TEM,int depth,int flag){NodeLink *temp=new NodeLink;for(int i=0;i<=8;i++)temp->boardstate.pos[i]=TEM->boardstate.pos[i];switch(flag){case 1:{temp->boardstate.pos[0]--;temp->boardstate.pos[sgoal[temp->boardstate.pos[0]]]++; //向左移break;}case 2:{temp->boardstate.pos[0]++;temp->boardstate.pos[sgoal[temp->boardstate.pos[0]]]--; //向右移break;}case 3:{temp->boardstate.pos[0]-=3;temp->boardstate.pos[sgoal[temp->boardstate.pos[0]]]+=3; //向上移break;}case 4:{temp->boardstate.pos[0]+=3;temp->boardstate.pos[sgoal[temp->boardstate.pos[0]]]-=3; //向下移break;}}temp->boardstate.d=depth+1;setboard(sgoal,temp->boardstate.pos,1);temp->boardstate.f=temp->boardstate.d+calvalue(sgoal);temp->boardstate.e=flag;temp->parent=TEM;return temp;}//把新节点加入OPEN队列NodeLink *addnode(NodeLink *head,NodeLink *node) //把node插入到head链中{NodeLink *TEM;TEM=head;head=node;head->next=TEM;head->previous=NULL;if(TEM)TEM->previous=head; //TEM已为空,无需操作return head;}//求启发值最小的结点NodeLink *minf(NodeLink *head){NodeLink *min,*forward;min=head;forward=head;while(forward){if(min->boardstate.f>forward->boardstate.f)min=forward;forward=forward->next;}return min;}int main( ){int depth=0;int source[9];int i,j;NodeLink *OPEN=new NodeLink;NodeLink *TEMP,*TEM;printf("请输入初始状态:\n");for(i=0;i<9;i++)scanf("%d",&source[i]);setboard(source,OPEN->boardstate.pos,0);OPEN->boardstate.d=depth;OPEN->boardstate.e=0;OPEN->boardstate.f=depth+calvalue(source);OPEN->next=NULL;OPEN->previous=NULL;OPEN->parent=NULL;while(OPEN){TEMP=minf(OPEN); //求具有最小启发值的节点setboard(sgoal,TEMP->boardstate.pos,1); //写棋盘if(!calvalue(sgoal))break;if(TEMP!=OPEN) //如果不是第一个节点{TEMP->previous->next=TEMP->next;TEMP->next->previous=TEMP->previous;}else//是第一个节点{if(OPEN->next) //如果还有节点{OPEN=OPEN->next;OPEN->previous=NULL;}else OPEN=NULL; //否则置为空}if(TEMP->boardstate.pos[0]-1>=0&&TEMP->boardstate.e!=2) //防止棋子回到原状态 OPEN=addnode(OPEN,makenode(TEMP,depth,1));if(TEMP->boardstate.pos[0]+1<=8&&TEMP->boardstate.e!=1)OPEN=addnode(OPEN,makenode(TEMP,depth,2));if(TEMP->boardstate.pos[0]-3>=0&&TEMP->boardstate.e!=4)OPEN=addnode(OPEN,makenode(TEMP,depth,3));if(TEMP->boardstate.pos[0]+3<=8&&TEMP->boardstate.e!=3)OPEN=addnode(OPEN,makenode(TEMP,depth,4));depth++;}if(OPEN) //如有解,则打印出解的步骤{printf("共需%d 步即可完成。
图搜索与问题求解(八数码)
图搜索与问题求解实验报告一实验题目图搜索与问题求解二实验目的1熟悉和掌握启发式搜索/A*搜索的定义、估价函数和算法过程;2 理解和掌握搜索过程,能够用选定的编程语言求解八数码问题,理解求解流程和搜索顺序;3 比较并分析图搜索策略的实质,通过实验理解启发式搜索/A*搜索的意义。
三实验要求1以九宫问题/八数码问题为例,以某种启发式搜索/A*搜索策略编程演示其搜索过程;2 定义启发式函数,能正确求解出从初始状态到目标状态的移动路线;3 对不可达状态能进行正确识别;4对所采用的启发式函数做出性能分析。
四数据结构typedef struct Qnode{ //队列的节点类型定义long a; //将8数码转化为长整型后入队列int dnum; //与目标状态数码不同的位置的个数Qnode *next;}*QueuePtr;typedef struct{QueuePtr front; //队头指针QueuePtr rear; //队尾指针}LinkQueue; //链式队列五实验算法1 说明有解和无解如何判定;int NiXu(int a[][3]) //求出所给状态的逆序数{i nt i,j,k=0,sum=0;i nt b[8];f or(i=0;i<3;i++)for(j=0;j<3;j++)if(a[i][j]) //空格用0代替,逆序不计空格b[k++]=a[i][j];for(i=1;i<8;i++)for(j=0;j<i;j++)if(b[i]<b[j])sum++;return sum;}if(NiXu(start)%2 != NiXu(end)%2)printf("无法到达!\n");e lse{printf("广度优先搜索如下:\n\n");search();}2 说明启发式函数如何设定;int h(long x){i nt sum=0;i nt b[3][3];u_trans(x,b);f or (int i=0;i<3;i++)for (int j=0;j<3;j++)if (end[i][j]!=b[i][j])sum++;r eturn sum;}3说明实验中采用的搜索算法。
启发式搜索算法解决八数码问题(C语言)
1、程序源代码#include <stdio.h>#include<malloc.h>struct node{int a[3][3];//用二维数组存放8数码int hx;//函数h(x)的值,表示与目标状态的差距struct node *parent;//指向父结点的指针struct node *next;//指向链表中下一个结点的指针};//------------------hx函数-------------------//int hx(int s[3][3]){//函数说明:计算s与目标状态的差距值int i,j;int hx=0;int sg[3][3]={1,2,3,8,0,4,7,6,5};for(i=0;i<3;i++)for(j=0;j<3;j++)if(s[i][j]!=sg[i][j])hx++;return hx;}//-------------hx函数end----------------------////-------------extend扩展函数----------------//struct node *extend(node *ex){ //函数说明:扩展ex指向的结点,并将扩展所得结点组成一条//单链表,head指向该链表首结点,并且作为返回值int i,j,m,n; //循环变量int t; //临时替换变量int flag=0;int x[3][3];//临时存放二维数组struct node *p,*q,*head;head=(node *)malloc(sizeof(node));//headp=head;q=head;head->next=NULL;//初始化for(i=0;i<3;i++)//找到二维数组中0的位置{for(j=0;j<3;j++)if(ex->a[i][j]==0){flag=1;break;}if(flag==1)break;}for(m=0;m<3;m++)//将ex->a赋给xfor(n=0;n<3;n++)x[m][n]=ex->a[m][n];//根据0的位置的不同,对x进行相应的变换//情况1if(i-1>=0){t=x[i][j];x[i][j]=x[i-1][j];x[i-1][j]=t;flag=0;for(m=0;m<3;m++)//将x赋给afor(n=0;n<3;n++)if(x[m][n]==ex->parent->a[m][n])flag++;if(flag!=9){q=(node *)malloc(sizeof(node));for(m=0;m<3;m++)//将x赋给afor(n=0;n<3;n++)q->a[m][n]=x[m][n];q->parent=ex;q->hx=hx(q->a);q->next=NULL;p->next=q;p=p->next;}}//情况2for(m=0;m<3;m++)//将ex->a重新赋给x,即还原x for(n=0;n<3;n++)x[m][n]=ex->a[m][n];if(i+1<=2){t=x[i][j];x[i][j]=x[i+1][j];x[i+1][j]=t; flag=0;for(m=0;m<3;m++)for(n=0;n<3;n++)if(x[m][n]==ex->parent->a[m][n])flag++;if(flag!=9){q=(node *)malloc(sizeof(node));for(m=0;m<3;m++)//将x赋给afor(n=0;n<3;n++)q->a[m][n]=x[m][n];q->parent=ex;q->hx=hx(q->a);q->next=NULL;p->next=q;p=p->next;}}//情况3for(m=0;m<3;m++)//将ex->a重新赋给x,即还原x for(n=0;n<3;n++)x[m][n]=ex->a[m][n];if(j-1>=0){t=x[i][j];x[i][j]=x[i][j-1];x[i][j-1]=t;flag=0;for(m=0;m<3;m++)for(n=0;n<3;n++)if(x[m][n]==ex->parent->a[m][n])flag++;if(flag!=9){q=(node *)malloc(sizeof(node));for(m=0;m<3;m++)//将x赋给afor(n=0;n<3;n++)q->a[m][n]=x[m][n];q->parent=ex;q->hx=hx(q->a);q->next=NULL;p->next=q;p=p->next;}}//情况4for(m=0;m<3;m++)//将ex->a重新赋给x,即还原xfor(n=0;n<3;n++)x[m][n]=ex->a[m][n];if(j+1<=2){t=x[i][j];x[i][j]=x[i][j+1];x[i][j+1]=t;flag=0;for(m=0;m<3;m++)for(n=0;n<3;n++)if(x[m][n]==ex->parent->a[m][n])flag++;if(flag!=9){q=(node *)malloc(sizeof(node));for(m=0;m<3;m++)for(n=0;n<3;n++)q->a[m][n]=x[m][n];q->parent=ex;q->hx=hx(q->a);q->next=NULL;p->next=q;p=p->next;}}head=head->next;return head;}//---------------extend函数end-----------------------////----------------insert函数-------------------------//node* insert(node *open,node * head){ //函数说明:将head链表的结点依次插入到open链表相应的位置, //使open表中的结点按从小到大排序。
人工智能-启发式搜索
实验报告姓名:***学号:**********班级:软件二班实验名称:启发式搜索课程名称:人工智能实验日期:2015.11.09实验环境:Visual C++实验目的以及内容:1、实验内容:使用启发式搜索算法求解八数码问题。
(1)编制程序实现求解八数码问题A*算法,采用估价函数其中:d(n)是搜索树中节点n的深度;w(n)为节点n的数据库中错放的棋子个数;p(n)为节点n的数据库中的每个棋子与其目标位置之间的距离的总和。
(2)分析上述(1)中的两种估价函数求解八数码问题的效率差别,给出一个是p(n)的上界的h(n)的定义,并测试使用该估价函数是否使算法失去可采纳性。
2、实验目的:熟练的掌握启发式搜索A*算法及其可采纳性。
3. 实验原理:八数码问题是在3行和3列构成的九宫棋盘上放置数码为1到8的8个棋盘,剩下一个空格的移动来不断改变棋盘的布局,求解这类问题的方法是:给定初始布局(即初始状态)和目标布局(即目标状态),定义操作算子的直观方法是为每个棋牌制定一套可能的走步》上,下,左,右四种移动,再根据所定义的启发式搜索函数在搜索过程中选择最合适的操作算子,得到最优的路径。
代码:#include"stdio.h"#define num 3void show(int begin[num][num]){for(int i = 0; i < num; i++){for(int j = 0; j < num; j++)printf("%d ", begin[i][j]);printf("\n");}printf("\n");}void exchange(int begin[num][num], int row_one, int column_one, int row_two, int column_two){int temp;temp = begin[row_two][column_two] ;begin[row_two][column_two] = begin[row_one][column_one];begin[row_one][column_one] = temp;}int judge(int begin[num][num], int end[num][num]){int count=0;for(int i = 0; i < num; i++)for(int j = 0; j < num; j++){if(begin[i][j] == end[i][j] && end[i][j] != 0)count++;}return count;}int yidong(int begin[num][num], int end[num][num], int right, int jishu, int ji_shu[50][3][3], int biaoji, int row, int column){int temp_zhi;show(begin);if(jishu >= 20)return 0;int node;int temp;for(int q=0; q<jishu; q++){node = 1;for(int w=0; w<num && node; w++)for(int r=0; r<num && node; r++)if(ji_shu[q][w][r] != begin[w][r])node = 0;if(node == 1){return 0;}}for(int i = 0; i < num; i++)for(int j = 0; j < num; j++)ji_shu[jishu][i][j] = begin[i][j];if(right == num * num - 1)return 1;if(row > 0 && biaoji != 0){exchange(begin, row - 1, column, row , column);temp = judge(begin, end);if(temp < right)exchange(begin, row - 1, column, row , column);else if(temp >= right){temp_zhi = yidong(begin, end, temp, jishu+1, ji_shu, 2, row-1, column);if( temp_zhi == 1)return 1;exchange(begin, row - 1, column, row , column);}}if(column > 0 && biaoji != 1){exchange(begin, row, column - 1, row , column);temp = judge(begin, end);if(temp < right)exchange(begin, row, column - 1, row , column);else if(temp >= right){temp_zhi = yidong(begin, end, temp, jishu+1, ji_shu ,3, row, column -1);if(temp_zhi == 1)return 1;exchange(begin, row, column - 1, row , column);}}if(row < num-1 && biaoji != 2){exchange(begin, row + 1, column, row , column);temp = judge(begin, end);if(temp < right)exchange(begin, row + 1, column, row , column);else if(temp >= right){temp_zhi =yidong(begin, end, temp, jishu+1, ji_shu, 0, row+1, column);if(temp_zhi == 1)return 1;exchange(begin, row + 1, column, row , column);}}if(column < num-1 && biaoji != 3){exchange(begin, row, column + 1, row , column);temp = judge(begin, end);if(temp < right)exchange(begin, row, column + 1, row , column);else if(temp >= right){temp_zhi = yidong(begin, end, temp, jishu+1, ji_shu, 1, row, column+1);if(temp_zhi == 1)return 1;exchange(begin, row, column + 1, row , column);}}return 0;}void shuru(int begin[][num],int blank[]){int temp, node, zero = 0;for (int i = 0; i < num; i++)for(int j = 0; j < num; j++){node = 1;printf("请输入第%d行,第%d列的元素的值:", i+1, j+1);scanf("%d", &temp);for (int q = 0; q <= i && node == 1; q++)for (int w = 0; w < j; w++)if(temp == begin[q][w]){printf("输入重复,请重新输入\n");node = 0;j--;break;}if(temp < 0 || temp > num*num-1){printf("请输入从%d到%d的数\n", zero, num*num-1);node = 0;j--;}if(node == 1){if(temp == 0){blank[0] = i;blank[1] = j;}begin[i][j] = temp;}}}int main(){int jishu = 0, ji_shu[50][3][3];int row;int column;int begin[num][num], blank[2],count=1;int end[num][num] = {1, 2, 3, 8, 0, 4, 7, 6, 5};printf ("-------8数码游戏开始!--------\n");shuru(begin, blank);row = blank[0];column = blank[1];if(yidong (begin, end,judge(begin,end),jishu,ji_shu,4,row,column) == 0)printf("\n此8数码的问题可能无解!");elseshow(begin);getchar();getchar();return 0;}实验截图:实验总结:1、A*搜索算法:取g(n)=d(n),h(n)=w(n),其中w(n)表示以目标为基准,结点n的状态中每一个数码牌与其目标位置之间的距离(不考虑夹在其间的数码牌)的总和,由于从结点n转换成目标结点至少需要w(n)步,所以对任意n,恒有w(n) ≤h*(n)。
人工智能:八数码难题的启发式搜索
人工智能基础大作业----八数码难题学院:数学与计算机科学学院2016.12.20一、实验名称八数码难题的启发式搜索二、实验目的八数码问题:在3×3的方格棋盘上,摆放着1到8这八个数码,有1个方格是空的,其初始状态如图1所示,要求对空格执行空格左移、空格右移、空格上移和空格下移这四个操作使得棋盘从初始状态到目标状态。
要求:1.熟悉人工智能系统中的问题求解过程;2.熟悉状态空间的启发式搜索算法的应用;3.熟悉对八数码问题的建模、求解及编程语言的应用。
三、实验设备及软件环境1.实验编程工具:VC++ 6.02.实验环境:Windows7 64位四、实验方法:启发式搜索1.算法描述1.将S放入open表,计算估价函数f(s)2.判断open表是否为空,若为空则搜索失败,否则,将open表中的第一个元素加入close表并对其进行扩展(每次扩展后加入open表中的元素按照代价的大小从小到大排序,找到代价最小的节点进行扩展)注:代价的计算公式f(n)=d(n)+w(n).其中f(n)为总代价,d(n)为节点的度,w(n)用来计算节点中错放棋子的个数。
判断i是否为目标节点,是则成功,否则拓展i,计算后续节点f(j),利用f(j)对open表重新排序2.算法流程图:3.程序源代码:# include<stdio.h># include<string.h># include<malloc.h># include<stdlib.h>typedef struct node {int i,cost,degree,exp,father;int a[3][3];struct node *bef,*late;struct node *son;}treenode;int flag=0,count=1,num=0,i=0;void set(treenode *s);void cpynode(treenode *s1,treenode *s2);void add1(treenode *s,treenode *open);void adjust1(treenode *close);void jscost(treenode *s);void tiaozheng(treenode *open);void sortopen(treenode *open);int test(treenode *s1,treenode *s2);void position(treenode *s,treenode *open,treenode*close,treenode *s1);void printstr(treenode *open);int search(treenode *s1,treenode *s2);void input(treenode *s);int cmpnode(treenode *s1,treenode *s2);void print(treenode *s);void add(treenode *s,treenode *close);void xuhao(treenode *s);void extend(treenode *r1,treenode *s,treenode *s1,treenode *open,treenode *close);void main() {treenode *s0,*s1,*s;treenode *open,*close,*opend,*closed;open=(treenode*)malloc(sizeof(treenode));close=(treenode*)malloc(sizeof(treenode));open->late=NULL;close->late=NULL;opend=open;closed=close;s0=(treenode*)malloc(sizeof(treenode));set (s0);s1=(treenode*)malloc(sizeof(treenode));set(s1);printf("请输入八数码的初始状态:(以空格为分隔)\n");input (s0);printf("请输入八数码的目标状态:(以空格为分隔)\n");input(s1);xuhao(s0);add (s0,opend);while(open->late!=NULL && flag==0) {s=(treenode*)malloc(sizeof(treenode));cpynode(s,open->late);open=open->late;add(s,close);if(test(s,s1)==0){flag=1; }else{position(s,open,close,s1);sortopen(open); };};if(open->late!=NULL) {printf("搜索过程如下:\n ");adjust1(close);printstr(close);printf("\n%d 步,%d 个节点\n",num,count);} else {printf("查找错误 ! \n");}; }void set(treenode *s) {s->i=i;s->father=0;s->degree=0;s->bef=NULL;s->son=NULL;s->late=NULL; };void input(treenode *s) {int j,k;for(j=0;j<3;j++)for(k=0;k<3;k++)scanf("%d",&s->a[j][k]); };int cmpnode(treenode *s1,treenode *s2){ int j,k;for(j=0;j<3;j++)for(k=0;k<3;k++) {if(s1->a[j][k]!=s2->a[j][k])return 0; };return 1; }int test(treenode *s1,treenode *s2) { int j,k,n=0;for(j=0;j<3;j++)for(k=0;k<3;k++) {if(s1->a[j][k]!=s2->a[j][k])n++; };s1->exp=n;return n; };void xuhao(treenode *s) {i++;s->i=i; }void cpynode(treenode *s1,treenode *s2) { int j,k;for(j=0;j<3;j++)for(k=0;k<3;k++)s1->a[j][k]=s2->a[j][k];s1->bef=s2->bef;s1->cost=s2->cost;s1->exp=s2->exp;s1->degree=s2->degree;s1->i=s2->i;s1->father=s2->father; };void print(treenode *s) {int j,k;for(j=0;j<3;j++) {for(k=0;k<3;k++) {printf("%2d",s->a[j][k]); }if(j==1) printf(" n=%2d d=%2df=%2d",s->i,s->degree,s->father);printf("\n"); }printf("\n"); }void position(treenode *s,treenode *open,treenode *close,treenode *s1) {int m,n,t,k;treenode *r1;for(m=0;m<3;m++) {for(n=0;n<3;n++) {k=s->a[m][n];if(k==0)break; };if(k==0) break; }if(m+1<=2&&flag==0) {r1=(treenode*)malloc(sizeof(treenode));cpynode(r1,s);t=r1->a[m+1][n];r1->a[m+1][n] = r1->a[m][n];r1->a[m][n]=t;extend(r1,s,s1,open,close); };if(m-1>=0&&flag==0) {r1=(treenode*)malloc(sizeof(treenode));cpynode(r1,s);t=r1->a[m-1][n];r1->a[m-1][n]=r1->a[m][n];r1->a[m][n]=t;extend(r1,s,s1,open,close); };if(n-1>=0 && flag==0) {r1=(treenode*)malloc(sizeof(treenode));cpynode(r1,s);t=r1->a[m][n-1];r1->a[m][n-1]=r1->a[m][n];r1->a[m][n]=t;extend(r1,s,s1,open,close); };if(n+1<=2 && flag==0) {r1=(treenode*)malloc(sizeof(treenode));cpynode(r1,s);t=r1->a[m][n+1];r1->a[m][n+1]=r1->a[m][n];r1->a[m][n]=t;extend(r1,s,s1,open,close); }; }void printstr(treenode *s) {treenode *t;t=s->late;while(t!=NULL) {num++;print(t);t=t->son; }; }void extend(treenode *r1,treenode *s,treenode *s1,treenode *open,treenode *close) {r1->father=s->i;r1->degree=s->degree+1;if(test(r1,s1)!=0) {jscost(r1);if(search(r1,close)==1 && search(r1,open)==1) { xuhao(r1);add1(r1,open);r1->bef=s;count++; }else free(r1); }else {xuhao(r1);jscost(r1);count++;add(r1,close);r1->bef=s;flag=1; } }int search(treenode *s1,treenode *close) { treenode *r,*t;r=s1;t=close->late;while(t!=NULL) {if(r->exp==t->exp) {if(cmpnode(r,t)==1)return 0; };t=t->late; };return 1; }void add(treenode *s,treenode *close) { treenode *r,*t;t=s;r=close;while(r->late!=NULL)r=r->late;r->late=t;t->late=NULL; }void add1(treenode *s,treenode *open){ treenode *t;t=open;s->late=t->late;t->late=s; }void adjust1(treenode *close) {treenode *s,*t;s=close;s->late->bef=NULL;while(s->late!=NULL)s=s->late;s->son=NULL;while(s->bef!=NULL) {t=s->bef;t->son=s;s=s->bef; }; }void jscost(treenode *s) {s->cost=(s->exp)+s->degree; }void sortopen(treenode *open) {treenode *t,*s,*r;int k;r=(treenode*)malloc(sizeof(treenode));t=open->late;while(t!=NULL && t->late!=NULL) {s=t->late;k=t->cost;while(s!=NULL) {if(k > s->cost) {k=s->cost;cpynode(r,t);cpynode(t,s);cpynode(s,r); }s=s->late; }t=t->late; }; }五、实验结果:1.程序截图2.搜索过程请输入八数码的初始状态:(以空格为分隔)2 8 31 0 47 6 5请输入八数码的目标状态:(以空格为分隔)1 2 38 0 47 6 5搜索过程如下:2 8 31 0 4 n= 1 d= 0 f= 07 6 52 0 31 8 4 n= 3 d= 1 f= 17 6 50 2 31 8 4 n= 8 d=2 f= 37 6 51 2 30 8 4 n=10 d= 3 f= 87 6 51 2 38 0 4 n=12 d= 4 f=107 6 55 步,12 个节点Press any key to continue六、实验分析:在进行搜索的过程中,同时记录了扩展新节点的个数。
人工智能实验报告-八数码(五篇模版)
人工智能实验报告-八数码(五篇模版)第一篇:人工智能实验报告-八数码《人工智能》实验一题目实验一启发式搜索算法1.实验内容:使用启发式搜索算法求解8数码问题。
⑴ 编制程序实现求解8数码问题A*算法,采用估价函数⎧⎪w(n),f(n)=d(n)+⎨pn⎪⎩()其中:d(n)是搜索树中结点n的深度;w(n)为结点n的数据库中错放的棋子个数;p(n)为结点n的数据库中每个棋子与其目标位置之间的距离总和。
⑵ 分析上述⑴中两种估价函数求解8数码问题的效率差别,给出一个是p(n)的上界的h(n)的定义,并测试使用该估价函数是否使算法失去可采纳性。
2.实验目的熟练掌握启发式搜索A算法及其可采纳性。
3.数据结构与算法设计该搜索为一个搜索树。
为了简化问题,搜索树节点设计如下:typedef struct Node//棋盘 {//节点结构体int data[9];double f,g;struct Node * parent;//父节点}Node,*Lnode;int data[9];数码数组:记录棋局数码摆放状态。
struct Chess * Parent;父节点:指向父亲节点。
下一步可以通过启发搜索算法构造搜索树。
1、局部搜索树样例:*2、搜索过程搜索采用广度搜索方式,利用待处理队列辅助,逐层搜索(跳过劣质节点)。
搜索过程如下:(1)、把原棋盘压入队列;(2)、从棋盘取出一个节点;(3)、判断棋盘估价值,为零则表示搜索完成,退出搜索;(4)、扩展子节点,即从上下左右四个方向移动棋盘,生成相应子棋盘;(5)、对子节点作评估,是否为优越节点(子节点估价值小于或等于父节点则为优越节点),是则把子棋盘压入队列,否则抛弃;(5)、跳到步骤(2);3、算法的评价完全能解决简单的八数码问题,但对于复杂的八数码问题还是无能为力。
现存在的一些优缺点。
1、可以改变数码规模(N),来扩展成N*N的棋盘,即扩展为N 数码问题的求解过程。
八数码问题的启发式函数
八数码问题的启发式函数
八数码问题是一个经典的搜索问题,通常使用启发式搜索算法来解决。
在八数码问题中,给定一个包含1-8八个数字的3x3格子的方阵,初
始时方阵的数字是打乱的,需要通过滑动数字使得方阵最终呈现1-8
的顺序。
启发式函数是用于评估搜索节点(状态)的优劣程度的函数,它可以
帮助搜索算法跳出局部最优解,提高搜索效率。
在八数码问题中,一个常用的启发式函数是基于曼哈顿距离的。
具体
来说,对于一个3x3的方阵,我们可以将其看作一个27维的向量,其
中每个数字对应一个维度。
对于任意两个状态(方阵),我们可以计
算它们对应向量之间的曼哈顿距离,将其作为启发式函数值。
曼哈顿距离的计算方式如下:对于任意两个状态A和B,假设它们的对应向量分别为a和b,则A和B之间的曼哈顿距离为:
d(A, B) = Σ |a[i] - b[i]|
其中i表示向量中的第i个元素。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
启发式搜索1. 介绍八数码问题也称为九宫问题。
在3×3的棋盘,摆有八个棋子,每个棋子上标有1至8的某一数字,不同棋子上标的数字不相同。
棋盘上还有一个空格(以数字0来表示),与空格相邻的棋子可以移到空格中。
要求解决的问题是:给出一个初始状态和一个目标状态,找出一种从初始转变成目标状态的移动棋子步数最少的移动步骤。
所谓问题的一个状态就是棋子在棋盘上的一种摆法。
解八数码问题实际上就是找出从初始状态到达目标状态所经过的一系列中间过渡状态。
2. 使用启发式搜索算法求解8数码问题。
1) A ,A 星算法采用估价函数()()()()w n f n d n p n ⎧⎪=+⎨⎪⎩, 其中:()d n 是搜索树中结点n 的深度;()w n 为结点n 的数据库中错放的棋子个数;()p n 为结点n 的数据库中每个棋子与其目标位置之间的距离总和。
2)宽度搜索采用f(i)为i 的深度,深度搜索采用f(i)为i 的深度的倒数。
3. 算法流程① 把起始节点S 放到OPEN 表中,并计算节点S 的)(S f ;② 如果OPEN 是空表,则失败退出,无解;③ 从OPEN 表中选择一个f 值最小的节点i 。
如果有几个节点值相同,当其中有一个 为目标节点时,则选择此目标节点;否则就选择其中任一个节点作为节点i ; ④ 把节点i 从 OPEN 表中移出,并把它放入 CLOSED 的已扩展节点表中; ⑤ 如果i 是个目标节点,则成功退出,求得一个解;⑥ 扩展节点i ,生成其全部后继节点。
对于i 的每一个后继节点j :计算)(j f ;如果j 既不在OPEN 表中,又不在CLOCED 表中,则用估价函数f 把 它添入OPEN 表中。
从j 加一指向其父节点i 的指针,以便一旦找到目标节点时记住一个解答路径;如果j 已在OPEN 表或CLOSED 表中,则比较刚刚对j 计算过的f 和前面计算过的该节点在表中的f 值。
如果新的f 较小,则(I)以此新值取代旧值。
(II)从j 指向i ,而不是指向他的父节点。
(III)如果节点j 在CLOSED 表中,则把它移回OPEN 表中。
⑦ 转向②,即GOTO ②。
4. 估价函数计算一个节点的估价函数,可以分成两个部分:1、 已经付出的代价(起始节点到当前节点);2、 将要付出的代价(当前节点到目标节点)。
节点n 的估价函数)(n f 定义为从初始节点、经过n 、到达目标节点的路径的最小代价的估计值,即)(*n f = )(*n g + )(*n h 。
)(*n g 是从初始节点到达当前节点n 的实际代价;)(*n h 是从节点n 到目标节点的最佳路径的估计代价,体现出搜索过程中采用的启发式信息(背景知识),称之为启发函数。
)(*n g 所占的比重越大,越趋向于宽度优先或等代价搜索;反之,)(*n h 的比重越大,表示启发性能就越强。
5. 实验代码为方便起见,目标棋局为不变(1)以下代码估价函数为深度+错放棋子个数 (2) 若估价函数为深度+每个棋子与其目标位置之间的距离总和,则加入估价函数 int calvalue1(int a[]) //不在位棋子数{int c = 0;int b=0;for (int i = 0;i <= 8;i++)for (int j = 0;j <= 8;j++)if (a[i] = goal[j])if (goal[j] != 0)c=c+abs(i%3-j%3)+abs((i- i%3)/3+(j- j%3)/3);return c;}(3)宽度搜索采用OPEN->jiedian.f = depth;(4) 深度搜索采用OPEN->jiedian.f = -depth;源代码:1. #include "stdio.h"2.3. int goal[9] = { 1,2,3,8,0,4,7,6,5 }, sgoal[9];//goal 为棋盘的目标布局,并用中间状态sgoal 与之比较4.5.struct Board6.{7.int shuzu[9];8.int d, f, e;//d:深度;f:启发函数;e:记录前一次的扩展节点9.};10.11.struct NodeLink12.{13.Board jiedian;14.NodeLink *parent;15.NodeLink *previous;16.NodeLink *next;17.NodeLink *path;18.};19.//更新纪录八数码的状态20.void setboard(int a[], int b[], int flag) //flag=0,写棋子;flag=1,写棋盘21.{22.for (int i = 0;i <= 8;i++)23.if (flag)24.a[b[i]] = i;25.else26.b[a[i]] = i;27.}28.//计算启发值的函数29.int calvalue(int a[]) //不在位棋子数30.{31.int c = 0;32.for (int i = 0;i <= 8;i++)33.if (a[i] != goal[i])34.if (goal[i] != 0)35.c++;36.return c;37.}38.//生成一个新节点的函数39.NodeLink *newnode(NodeLink *TEM, int depth, int flag)40.{41.NodeLink *temp = new NodeLink;42.for (int i = 0;i <= 8;i++)43.temp->jiedian.shuzu[i] = TEM->jiedian.shuzu[i];44.switch (flag)45.{46.case 1:47.{48.temp->jiedian.shuzu[0]--;49.temp->jiedian.shuzu[sgoal[temp->jiedian.shuzu[0]]]++; //向左移51.}52.case 2:53.{54.temp->jiedian.shuzu[0]++;55.temp->jiedian.shuzu[sgoal[temp->jiedian.shuzu[0]]]--; //向右移56.break;57.}58.case 3:59.{60.temp->jiedian.shuzu[0] -= 3;61.temp->jiedian.shuzu[sgoal[temp->jiedian.shuzu[0]]] += 3; //向上移62.break;63.}64.case 4:65.{66.temp->jiedian.shuzu[0] += 3;67.temp->jiedian.shuzu[sgoal[temp->jiedian.shuzu[0]]] -= 3; //向下移68.break;69.}70.}71.temp->jiedian.d = depth + 1;72.setboard(sgoal, temp->jiedian.shuzu, 1);73.temp->jiedian.f = temp->jiedian.d + calvalue(sgoal);74.temp->jiedian.e = flag;75.temp->parent = TEM;76.return temp;77.}78.//把新节点加入OPEN队列79.NodeLink *addnode(NodeLink *head, NodeLink *node) //把node插入到head链中80.{81.NodeLink *TEM;82.TEM = head;83.head = node;84.head->next = TEM;85.head->previous = NULL;86.if (TEM)87.TEM->previous = head; //TEM已为空,无需操作88.return head;89.}90.91.//求启发值最小的结点92.NodeLink *minf(NodeLink *head)93.{94.NodeLink *min, *forward;96.forward = head;97.while (forward)98.{99.if (min->jiedian.f>forward->jiedian.f)100.min = forward;101.forward = forward->next;102.}103.return min;104.}105.106.int main()107.{108.int depth = 0;109.int source[9];110.int i, j;111.112.NodeLink *OPEN = new NodeLink;113.NodeLink *TEMP, *TEM;114.115.printf("请输入初始状态:\n");116.for (i = 0;i<9;i++)117.scanf_s("%d", &source[i]);118.119.setboard(source, OPEN->jiedian.shuzu, 0);120.OPEN->jiedian.d = depth;121.OPEN->jiedian.e = 0;122.OPEN->jiedian.f = depth + calvalue(source);123.OPEN->next = NULL;124.OPEN->previous = NULL;125.OPEN->parent = NULL;126.127.while (OPEN)128.{129.TEMP = minf(OPEN); //求具有最小启发值的节点130.setboard(sgoal, TEMP->jiedian.shuzu, 1); //写棋盘131.if (!calvalue(sgoal))132.break;133.if (TEMP != OPEN) //如果不是第一个节点134.{135.TEMP->previous->next = TEMP->next;136.TEMP->next->previous = TEMP->previous;137.}138.else //是第一个节点140.if (OPEN->next) //如果还有节点141.{142.OPEN = OPEN->next;143.OPEN->previous = NULL;144.}145.else OPEN = NULL; //否则置为空146.}147.148.if (TEMP->jiedian.shuzu[0] - 1 >= 0 && TEMP->jiedian.e != 2) //防止棋子回到原状态149.OPEN = addnode(OPEN, newnode(TEMP, depth, 1));150.if (TEMP->jiedian.shuzu[0] + 1 <= 8 && TEMP->jiedian.e != 1)151.OPEN = addnode(OPEN, newnode(TEMP, depth, 2));152.if (TEMP->jiedian.shuzu[0] - 3 >= 0 && TEMP->jiedian.e != 4)153.OPEN = addnode(OPEN, newnode(TEMP, depth, 3));154.if (TEMP->jiedian.shuzu[0] + 3 <= 8 && TEMP->jiedian.e != 3)155.OPEN = addnode(OPEN, newnode(TEMP, depth, 4));156.depth++;157.}158.159.if (OPEN) //如有解,则打印出解的步骤160.{161.TEMP->path = NULL;162.while (TEMP->parent) //每次回溯父节点,生成路径163.{164.TEMP->parent->path = TEMP;165.TEMP = TEMP->parent;166.}167.j = 0;168.while (TEMP->path)169.{170.setboard(sgoal, TEMP->jiedian.shuzu, 1);171.printf("第%d步:\n", j);172.for (i = 0;i <= 2;i++)173.printf(" %d", sgoal[i]);174.printf(" \n");175.for (i = 3;i <= 5;i++)176.printf(" %d", sgoal[i]);177.printf("\n");178.for (i = 6;i <= 8;i++)179.printf(" %d", sgoal[i]);180.printf("\n");181.TEMP = TEMP->path;182.j++;184.setboard(sgoal, TEMP->jiedian.shuzu, 1);185.printf("第%d步:\n", j);186.for (i = 0;i <= 2;i++)187.printf(" %d", sgoal[i]);188.printf("\n");189.for (i = 3;i <= 5;i++)190.printf(" %d", sgoal[i]);191.printf("\n");192.for (i = 6;i <= 8;i++)193.printf(" %d", sgoal[i]);194.printf("\n");195.}196.else197.printf("无法求解!");198.}(1)以上代码估价函数为深度+错放棋子个数(2) 若估价函数为深度+每个棋子与其目标位置之间的距离总和,则函数改为int calvalue(int a[]) //不在位棋子数{int c = 0;int b=0;for (int i = 0;i <= 8;i++)for (int j = 0;j <= 8;j++)if (a[i] = goal[j])if (goal[j] != 0)c=c+abs(i%3-j%3)+abs((i- i%3)/3+(j- j%3)/3);return c;}(3)宽度搜索采用OPEN->jiedian.f = depth;(4) 深度搜索采用OPEN->jiedian.f = -depth;6.输出结果:(输入为:)目标状态为:(1)估价函数为深度+错放棋子个数(2) 估价函数为深度+每个3棋子与其目标位置之间的距离总和(3)宽度搜索采用OPEN->jiedian.f = depth;(4) 深度搜索采用OPEN->jiedian.f = -depth;。