隐式图的搜索实验准备

1.从起点 开始,把它加入到一个由方格组成的open list(开放列表) 中,这个open list像是一个购物清单。Open list里的格子是可能会是沿途经过的,也有可能不经过。因此可以将其看成一个待检查的列表。查看与相邻的8个方格 ,把其中可走的 (walkable) 或可到达的(reachable) 方格加入到open list中。并把起点 设置为这些方格的父节点 (parent node) 。然后把 起点 从open list中移除,加入到close list(封闭列表) 中,close list中的每个方格都是不需要再关注的。
2.需要从open list中选一个与起点相邻的方格。但是到底选择哪个方格好呢?选F值(F(n) = G + H 。G代表的是从初始位置A沿着已生成的路径到指定待检测格子的移动开销。H指定待测格子到目标节点B的估计移动开销。H采用的是传统的曼哈顿距离(Manhattan Distance),也就是横纵向走的距离之和,并且忽略沿途的障碍。)最小的那个。
3.比较open list中节点的F值后,发现起点右侧节点的F=40,值最小。选作当前处理节点,并将这个点从Open List删除,移到Close List中。
4.对这个节点周围的8个格子进行判断,若是不可通过(比如墙,水,或是其他非法地形)或已经在Close List中,则忽略。否则执行以下步骤:
5.(1)若当前处理节点的相邻格子已经在Open List中,则检查这条路径是否更优,即计算经由当前处理节点到达那个方格是否具有更小的 G值。如果没有,不做任何操作。相反,如果G值更小,则把那个方格的父节点设为当前处理节点 ( 我们选中的方格 ) ,然后重新计算那个方格的 F 值和 G 值。(2)若当前处理节点的相邻格子不在Open List中,那么把它加入,并将它的父节点设置为该节点。
算法思想:
1.定义open表和close表,其中open表是用来存储待查验的节点,而close表是用来存储已查验过的节点(不需要再关注的节点)
2.把开始节点加入open表;
3.将开始节点拓展的子节点加入open表,将开始节点加入到close表;
4.将open表中的节点的耗散值也就是f进行从小到大的排序,此时open表中的第一个节点的耗散值最小,对open表中的第一个节点进行判断,如果这个节点是目标节点,则算法结束,无需进行以下步骤;反之,如果这个节点不是目标节点,则将这个节点进行扩展,再进行下一步;
5.判断n的可扩展节点(相邻节点)m,情况一:如果m在open表中,则说明初始节点到m节点出现了两条路径,此时需要判断这两条路径的耗散值的大小如果是新路径的耗散值小,则需要更改m节点的父节点,并将open表中的原的m节点加入close表,将现在的m节点加入open表,如果是新路径的耗散值大,则不需要进行任何操作;情况二:如果m在close表中,则说明初始节点该节点到m节点有两条路径,如果是新路径的耗散值大,则不需要进行任何操作如果是新路径的耗散值小,需要将更改m节点的父节点,并将m节点从close表中取出,并放入open表中;情况三:m节点既不在open表中也不在close表中,直接将m节点加入到open表中即可;
6.重复第4步,(算法结束的两种情况,其一,当前节点就是目标节点,即找到
了最优解;其二,open表为空,既无法找到到目标节点的路径,即无解。)

首先通过构建结点类,存储上下左右四个方向的状态,并用parent结点记录其原来位置,通过A算法的需要,构建评估价值的函数,即F(n)=g(n)+h(n)
其次构建隐式图的主要运作类
1.对是否可行的判断(奇偶性判断)放在一开始,若不可行,即不能到达目标位置,则直接,跳过。
2.构建判断两个二维数组是否相等的函数,作为结束的判断,以及中间查重的步骤。
3.构建g(n)函数,即两个二维数组有多少数字不相同。
4.构建主要的运行函数,通过对于四个方向的判断,增添其子节点。通过循环不断添加,直到达到目标位置。
5.输出函数,逆序输出,从到达的目标位置,不断寻找父结点存到一个新的列表里。
F(n)函数用于对列表再排序,找到价值最大的结点,寻找他的子节点。
实验任务:
1)对九宫重排问题,建立图的启发式搜索求解方法;
2)用A
算法求解九宫重排问题。

实验要求:
3х3九宫棋盘,放置数码为1~8的8个棋子,棋盘中留有一个空格,空格周围的棋子可以移动到空格中,从而改变棋盘的布局。根据给定初始布局和目标布局,移动棋子从初始布局到达目标布局,求解移动步骤并输出。请设计算法,使用合适的搜索策略,在较少的空间和时间代价下找到最短路径。

编程语言以及开发环境的选择
编程语言采用java,开发环境为idea

实验思路
对于8数码问题我们首先应该想到的是问题是否可解,如何判断呢?这里给出如下判定结论:
将状态表示成一维的形式,求出除0(空格)之外所有数字的逆序数之和,也就是每个数字前面比它大的数字的个数的和,称为这个状态的逆序数。若两个状态的逆序奇偶性相同,则可相互到达,否则不可相互到达[1]。
例如:

S0表示成: 2 X 3 1 8 4 7 6 5
则:f(2)=0, f(3)=0, f(1)=2, f(8)=0, f(4)=1, f(7)=1, f(6)=2, f(5)=3
Sg表示成: 1 2 3 8 X 4 7 6 5
则:f(2)=0, f(3)=0, f(1)=2, f(8)=0, f(4)=1, f(7)=1, f(6)=2, f(5)=3
当f(a8)+f(a7)+……+f(a1)均为奇数或偶数时才能重排成功,所以,S0到Sg状态有解。
判断是否可解的问题解决了,用什么样的搜索算法找到移动路径呢?

其实,不管哪种搜索,都可以统一用这样的形式表示: 搜索的对象是一个图,它面向一个问题,不一定有明确的存储形式,但它里面的每一个结点都有可能是一个解(可行解),搜索的目的有两个方面,或者求可行解,或者从可行解集中求最优解。

搜索算法可分为两大类:无信息的搜索算法和有信息的搜索算法。无信息的搜索又称盲目搜索,其特点是只要问题状态可以形式化表示,原则上就可用使用无信息的搜索,无信息搜索有如下常见的几种搜索策略:广度优先搜索、代价一致搜索、深度优先搜索、深度有限搜索、迭代深入优先搜索、双向搜索。我们说DFS和BFS都是蛮力搜索,因为它们在搜索到一个结点时,在展开它的后续结点时,是对它们没有任何‘认识’ 的,它认为它的孩子们都是一样的‘优秀’,但事实并非如此,后续结点是有好有坏的。好,就是说它离目标结点‘近’,如果优先处理它,就会更快的找到目标结点,从而整体上提高搜索性能。

为了改善上面的算法,我们需要对展开后续结点时对子结点有所了解,这里需要一个估值函数,估值函数就是评价函数,它用来评价子结点的好坏,因为准.确评价是不可能的,所以称为估值。这就是我们所谓的有信息搜索。如果估值函数只考虑结点的某种性能上的价值,而不考虑深度,比较有名的就是有序搜索(Ordered-Search),它着重看好能否找出解,而不看解离起始结点的距离(深度)。如果估值函数考虑了深度,或者是带权距离(从起始结点到目标结点的距离加权和),那就是A*。简单的来说A*就是将估值函数分成两个部分,一个部分是路径价值,另一个部分是一般性启发价值,合在一起算估整个结点的价值。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
include using namespace std; struct node{ int nodesun[4][4]; int pre; //上一步在队列中的位置 int flag ; //步数标识,表示当前的步数为有效的 int value; //与目标的差距 int x,y; //空格坐标 }queue[1000]; //移动方向数组 int zx[4]={-1,0,1,0}; int zy[4]={0,-1,0,1}; //当前步数 int top; int desti[4][4];//目标状态 int detect(struct node *p)//检查是否找到 {int i,j; for(i=1;i<4;i++) for(j=1;jnodesun[i][j]!=desti[i][j]) return 0; return 1; } //打印 void printlj() {int tempt; int i,j; tempt=top; while(tempt!=0) { for(i=1;i<4;i++) for(j=1;j<4;j++) {cout<<queue[tempt].nodesun[i][j]; if(j==3) cout<<" "<<endl; } tempt=queue[tempt].pre; } } //现在状态与目标状态有多少个不同位置 int VALUE(struct node *p) {int count=0; int i,j; for(i=1;i<4;i++) for(j=1;jnodesun[i][j]!=desti[i][j]) count++; return count; } void main() { //初始化 int i,j,m,n,f; int min=10; int temp,find=0,minnumber; top=1; for(i=1;i<4;i++) for(j=1;j<4;j++) {cout<<"请输入第"<<i<<"行"<<"第"<<j<<"列的值"<>temp; queue[1].nodesun[i][j]=temp; } cout<<"请输入初始状态的空格的位置(行)"<>temp; queue[1].x=temp; cout<<"请输入初始状态的空格的位置(列)"<>temp; queue[1].y=temp; queue[1].value=VALUE(&queue[1]); queue[1].pre=0; //上一步在队列中的位置 queue[1].flag=0; //目标状态 for(i=1;i<4;i++) for(j=1;j<4;j++) {cout<<"请输入目标状态第"<<i<<"行"<<"第"<<j<<"列的值"<>temp; desti[i][j]=temp; } //根据估价函数 while(!find&&top>0) { for(i=1;i<=top;i++) //////////////////////////////////////////// //min为上一中与目标有多少个元素不相同,queue[i]为当前目标有多少个元素不相同通过这两个数的比较,就可以得出当前较之上一目标接近同时把当前的i记录下来进行下一步比较 {if(queue[i].value<min&&queue[i].flag==0) {minnumber=i;// min=queue[i].value; //还有多少不同的位数 } } queue[minnumber].flag=1; //表示此位有效 ////////////////////////////////////// // for(f=0;f=1&&i=1&&j<=3) {top++; ///////////////////////////////////////////// //位置交换 queue[top]=queue[minnumber]; queue[top].nodesun[m][n]=queue[minnumber].nodesun[i][j]; queue[top].nodesun[i][j]=0; /////////////////////////////////////// //空格移动方向 queue[top].x=i; queue[top].y=j; /////////////////////////////////////// queue[top].pre=minnumber; //上一步在队列中的位置 queue[top].value=VALUE(&queue[top]); //有多少位与目标不同 queue[top].flag=0; //标识位初始化 if(detect(&queue[top])) //检查是否为目标 {printlj(); //打印 find=1; //设找到标识位 break; } } } } }

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值