状态空间搜索——八数码问题
实验报告
【注】源码将以附件的形式上传,其中EightPuzzle.java为vo类,EightPuzzleOperator.java为util类,EightPuzzleAlgorithm.java为算法实现类。Main函数在EightPuzzleAlgorithm.java类中。
【源码下载】
理解和掌握状态空间搜索的策略。
在一个3*3的九宫中有1~8个数及一个空格随机地摆放在其中的个子里,现在要求实现这个问题;将该九宫格调整为某种有序的形式。调整的规则是,每次只能将空格左上右下移动,试编程实现这一问题的求解。
用你们学过的某种语言编写程序,利用不同的搜索策略进行状态空间搜索(如宽度优先搜索、深度优先搜索、有界深度优先搜索、启发式搜索等)。
【实验测试结果】
测试中的一组数据:
请输入初始位置(其中输入0代表空白块,例如:2 8 3 1 0 4 7 6 5):
2 8 3 1 0 4 7 6 5
请输入目标位置(其中输入0代表空白块,例如:2 8 3 1 4 0 7 6 5):
2 8 3 1 4 0 7 6 5
深度优先搜索:
深度优先搜索方法路径!
2 8 3 1 0 4 7 6 5
2 8 3 0 1 4 7 6 5
2 8 3 7 1 4 0 6 5
2 8 3 7 1 4 6 0 5
2 8 3 7 1 4 6 5 0
2 8 3 7 1 0 6 5 4
2 8 3 7 0 1 6 5 4
2 8 3 0 7 1 6 5 4
2 8 3 6 7 1 0 5 4
2 8 3 6 7 1 5 0 4
2 8 3 6 7 1 5 4 0
2 8 3 6 7 0 5 4 1
2 8 3 6 0 7 5 4 1
2 8 3 0 6 7 5 4 1
2 8 3 5 6 7 0 4 1
2 8 3 5 6 7 4 0 1
2 8 3 5 6 7 4 1 0
2 8 3 5 6 0 4 1 7
2 8 3 5 0 6 4 1 7
2 8 3 0 5 6 4 1 7
2 8 3 4 5 6 0 1 7
2 8 3 4 5 6 1 0 7
2 8 3 4 5 6 1 7 0
2 8 3 4 5 0 1 7 6
2 8 3 4 0 5 1 7 6
2 8 3 0 4 5 1 7 6
2 8 3 1 4 5 0 7 6
2 8 3 1 4 5 7 0 6
2 8 3 1 4 5 7 6 0
2 8 3 1 4 0 7 6 5
终于找到了,⊙﹏⊙b汗
有界深度优先搜索:
有界深度优先搜索方法路径!
2 8 3 1 0 5 7 4 6
0 8 3 2 4 5 1 7 6
2 8 3 4 7 5 1 0 6
2 0 3 4 8 5 1 7 6
2 8 0 4 5 3 1 7 6
2 8 3 4 0 6 1 5 7
0 8 3 2 5 6 4 1 7
2 8 3 5 1 6 4 0 7
2 0 3 5 8 6 4 1 7
2 8 0 5 6 3 4 1 7
2 8 3 5 0 7 4 6 1
0 8 3 2 6 7 5 4 1
2 8 3 6 4 7 5 0 1
2 0 3 6 8 7 5 4 1
2 8 0 6 7 3 5 4 1
2 8 3 6 0 1 5 7 4
0 8 3 2 7 1 6 5 4
2 8 3 7 5 1 6 0 4
2 0 3 7 8 1 6 5 4
2 8 0 7 1 3 6 5 4
2 8 3 7 0 4 6 1 5
0 8 3 2 1 4 7 6 5
2 8 3 0 1 4 7 6 5
2 8 3 7 1 4 0 6 5
2 8 3 1 0 4 7 6 5
0 8 3 2 1 4 7 6 5
8 0 3 2 1 4 7 6 5
0 8 3 2 1 4 7 6 5
8 1 3 2 0 4 7 6 5
8 3 0 2 1 4 7 6 5
2 8 3 1 6 4 7 0 5
2 8 3 1 6 4 0 7 5
2 8 3 1 6 4 7 0 5
2 8 3 1 6 4 0 7 5
2 8 3 1 6 4 7 5 0
2 8 3 0 6 4 1 7 5
2 8 3 1 6 4 0 7 5
2 8 3 6 0 4 1 7 5
0 8 3 2 6 4 1 7 5
2 8 3 1 6 4 7 5 0
2 8 3 1 6 4 7 0 5
2 8 3 1 6 4 0 7 5
2 8 3 1 6 0 7 5 4
2 8 3 1 0 6 7 5 4
2 8 0 1 6 3 7 5 4
2 8 3 1 0 4 7 6 5
2 8 3 1 6 4 7 0 5
2 8 3 1 6 4 0 7 5
2 8 3 1 4 0 7 6 5
终于找到了,⊙﹏⊙b汗
广度优先搜索:
广度优先搜索方法路径!
2 8 3 1 0 4 7 6 5
2 0 3 1 8 4 7 6 5
2 8 3 1 4 0 7 6 5
终于找到了,⊙﹏⊙b汗
由于上面的运行结果截图不能一次截图完毕,所以将其中的运行过程复制了下来,上面的代码均为Java源码。下面分别对上面的三种算法使用自然语言描述。
【实验算法描述】
八数码问题,是对给定的一个初始位置,然后经过多次移动找到目标位置,并列举出其中的移动过程,最后可以找到既是可以成功,否则以失败告终。是一种过程中无人为参与的一中求解方法。
深度优先搜索:
深度优先搜索算法是按照一条路径一直往下深度延伸其子节点的算法,直到找到答案为止。也可能一直到达一个很深的深度之后还是没有找到问题的答案,这样就有可能出现栈溢出或者内存超界的情况(本实验中数字组合相对较少,不会造成内存超限的情况)。在深度优先搜索的求解过程中使用栈来存储还未搜索的节点,已经搜索过的节点使用一个链表来存储(避免重复的搜索,属于优化过程)。如果已经在链表中那么就不在放到栈中,因为之前已经搜索过了,不需要重复搜索。当然在深度优先搜索的过程中,不需要记录其深度,因为不会用深度来限制搜索。所以判断是不是已经包含当然搜索的节点在链表中,是用其中的数值数组是不是完全相同来判断的。
有界深度优先搜索:
有界深度优先搜索是在深度优先搜索的基础上进行的另一种对其深度进行限制的一种搜索方法。当然其使用的数据结构也是和深度优先搜索一样的,其中的不同之处在于,在有界深度优先搜索的基础上判断是不是相等时会有一个深度的同时相等的判断,即只有当其中的数组数值和搜索深度同时相等时才认为是相等的。同时也会在深度达到5以后搜索不会继续发展更深的子节点,而是开始搜索其兄弟节点等,然后继续。如果找不到就返回“非常遗憾,没有搜索到你需要的目标!%>_<%”。
广度(宽度)优先搜索:
广度优先搜索,也被称作宽度优先搜索,是首先在兄弟节点之间进行搜索和遍历的方法,然后知道其全部的兄弟节点都已经遍历完,才开始继续往下发展其子节点,所以在这个过程中使用队列(Queue接口的实现类LinkedList)来存储其中还没有遍历的但是已经发展了的节点,同时使用链表来存储已经搜索过的子节点,然后基本上是和深度优先搜索算法类似。如果找不到就返回“非常遗憾,没有搜索到你需要的目标!%>_<%”。
【总结】
本次实验利用图的搜素算法——深度优先搜索、广度优先搜索、有界深度优先搜索三种算法完成八数码问题的求解,利用Java程序设计语言编程,过程中遇到了诸多问题,以前太注重算法这一块的练习,实验中,每一块都是自己努力完成,也解决了遇到的问题,所以还是很有收获的。
package cn.edu.nwsuaf.qhs.artificialintelligence.eightpuzzle; import java.util.Arrays; public class EightPuzzle implements Cloneable{ /*利用一个二维的数组来存储数据*/ public int[][] data; private int blankPos_x,blankPos_y; private int depth; //无参构造函数 public EightPuzzle(){ data = new int[3][3]; } //传递一个数组,进行初始化的构造函数 public EightPuzzle(int [][] data){ this.data = data; } //判断是不是和目标位置相同 /*int[][] data1 = new int[][]{{1,2,3},{4,5,6},{7,8,9}}; int[][] data2 = new int[][]{{1,2,3},{4,5,6},{7,8,9}}; System.out.println("Equals--->"+Arrays.equals(data1, data2)); false System.out.println("deepEquals--->"+Arrays.deepEquals(data1, data2)); true*/ public boolean isEquals(EightPuzzle ep){ return Arrays.deepEquals(this.data, ep.data); } @Override public String toString(){ StringBuffer sb = new StringBuffer(20); for (int i = 0; i < 3; i++){ for (int j = 0; j < 3; j++){ sb.append(this.data[i][j]); sb.append(" "); } } return sb.toString(); } // 获取空格的位置 public void getPostion() { for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { if (this.data[i][j] == 0) { this.setBlankPos_x(i); this.setBlankPos_y(j); } } } } public void setBlankPos_x(int blankPos_x) { this.blankPos_x = blankPos_x; } public void setBlankPos_y(int blankPos_y) { this.blankPos_y = blankPos_y; } public int getBlankPos_x() { return blankPos_x; } public int getBlankPos_y() { return blankPos_y; } public int getDepth() { return depth; } public void setDepth(int depth) { this.depth = depth; } public void print(){ System.out.println(this.toString()); } //浅拷贝 @Override protected EightPuzzle clone() throws CloneNotSupportedException { // TODO Auto-generated method stub return new EightPuzzle(Arrays.copyOf(this.data, this.data.length)); } //深拷贝 public EightPuzzle depthClone(){ EightPuzzle tmp_ep = new EightPuzzle(); for (int i = 0 ; i < 3; i++) for (int j = 0 ; j < 3; j++) tmp_ep.data[i][j] = this.data[i][j]; tmp_ep.depth = this.depth; return tmp_ep; } public static void main(String[] args) { } @Override public boolean equals(Object obj) { // TODO Auto-generated method stub return this.isEquals((EightPuzzle)obj); //&&(this.getDepth() == ((EightPuzzle)obj).getDepth() } }
package cn.edu.nwsuaf.qhs.artificialintelligence.eightpuzzle; public class EightPuzzleOperator { //判断是不是可以继续移动 public static boolean canMove(int x, int y, int direction) { if ((direction == 1 && x == 0) || (direction == -1 && x == 2) || (direction == 2 && y == 2) || (direction == -2 && y == 0)) { return false; } return true; } //根据给出的参数,进行空格位置的移动 //其中1表示向上,2表示向右,-1表示向下,-2表示向左 public static EightPuzzle movePosition(EightPuzzle ep, int args) { EightPuzzle arg_ep = null; arg_ep = ep.depthClone(); arg_ep.getPostion(); int blankPos_x = arg_ep.getBlankPos_x(), blankPos_y = arg_ep .getBlankPos_y(); //指令为向上移动 if (args == 1) { int temp = arg_ep.data[blankPos_x][blankPos_y]; arg_ep.data[blankPos_x][blankPos_y] = arg_ep.data[blankPos_x - 1][blankPos_y]; arg_ep.data[blankPos_x - 1][blankPos_y] = temp; //表示移动成功 } //指令为向下移动 else if (args == -1) { int temp = arg_ep.data[blankPos_x][blankPos_y]; arg_ep.data[blankPos_x][blankPos_y] = arg_ep.data[blankPos_x + 1][blankPos_y]; arg_ep.data[blankPos_x + 1][blankPos_y] = temp; //表示移动成功 } //指令为向右移动 else if (args == 2) { int temp = arg_ep.data[blankPos_x][blankPos_y]; arg_ep.data[blankPos_x][blankPos_y] = arg_ep.data[blankPos_x][blankPos_y + 1]; arg_ep.data[blankPos_x][blankPos_y + 1] = temp; //表示移动成功 } //指令为向左移动 else if (args == -2) { int temp = arg_ep.data[blankPos_x][blankPos_y]; arg_ep.data[blankPos_x][blankPos_y] = arg_ep.data[blankPos_x][blankPos_y - 1]; arg_ep.data[blankPos_x][blankPos_y - 1] = temp; //表示移动成功 } //指令输入错误 else { return null; } return arg_ep; } }
package cn.edu.nwsuaf.qhs.artificialintelligence.eightpuzzle; import java.util.LinkedList; import java.util.Queue; import java.util.Scanner; import java.util.Stack; public class EightPuzzleAlgorithm { private int[][] array = new int[3][3]; private int[][] target = new int[3][3]; private EightPuzzle ep, target_ep; private int depth = 0; private Stack<EightPuzzle> ep_stack = new Stack<EightPuzzle>(); private LinkedList<EightPuzzle> searched_list = new LinkedList<EightPuzzle>(); private Queue<EightPuzzle> ep_queue = new LinkedList<EightPuzzle>(); public EightPuzzleAlgorithm() { // 初始化栈和队列,以及列表 ep_stack.clear(); searched_list.clear(); ep_queue.clear(); Scanner scanner = new Scanner(System.in); // 输入初始位置 System.out.println("请输入初始位置(其中输入0代表空白块,例如:2 8 3 1 0 4 7 6 5):"); for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { array[i][j] = scanner.nextInt(); } } // 输入目标位置 System.out.println("请输入目标位置(其中输入0代表空白块,例如:2 8 3 1 4 0 7 6 5):"); for (int i = 0; i < 3; i++) { for (int j = 0; j < 3; j++) { target[i][j] = scanner.nextInt(); } } ep = new EightPuzzle(array); ep.setDepth(depth); // 设置栈底元素 ep_stack.push(ep); target_ep = new EightPuzzle(target); scanner.close(); // 设置队首元素 ep_queue.offer(ep); } // 深度优先搜索 // 栈的方式实现 public void depthFirstSearch() { System.out.println("深度优先搜索方法路径!"); if (!searched_list.isEmpty()) searched_list.clear(); while (!ep_stack.isEmpty()) { EightPuzzle move_ep = ep_stack.pop(); depth = move_ep.getDepth(); move_ep.getPostion(); int x = move_ep.getBlankPos_x(), y = move_ep.getBlankPos_y(); move_ep.print(); searched_list.add(move_ep); if (move_ep.isEquals(target_ep)) { System.out.println("终于找到了,⊙﹏⊙b汗"); return; } depth++; EightPuzzle temp = null; temp = move_ep.depthClone(); if (EightPuzzleOperator.canMove(x, y, 1)) { temp = EightPuzzleOperator.movePosition(move_ep, 1); temp.setDepth(depth); if (!searched_list.contains(temp)) { ep_stack.push(temp); } } if (EightPuzzleOperator.canMove(x, y, 2)) { temp = EightPuzzleOperator.movePosition(move_ep, 2); temp.setDepth(depth); if (!searched_list.contains(temp)) { ep_stack.push(temp); } } if (EightPuzzleOperator.canMove(x, y, -1)) { temp = EightPuzzleOperator.movePosition(move_ep, -1); temp.setDepth(depth); if (!searched_list.contains(temp)) { ep_stack.push(temp); } } if (EightPuzzleOperator.canMove(x, y, -2)) { temp = EightPuzzleOperator.movePosition(move_ep, -2); temp.setDepth(depth); if (!searched_list.contains(temp)) { ep_stack.push(temp); } } } System.out.println("非常遗憾,没有搜索到你需要的目标!%>_<%"); } // 深度优先搜索(有界,最大深度为5) // 栈的方式实现搜索 public void boundedDepthFirstSearch() { System.out.println("有界深度优先搜索方法路径!"); if (!searched_list.isEmpty()) searched_list.clear(); while (!ep_stack.isEmpty()) { EightPuzzle move_ep = ep_stack.pop(); depth = move_ep.getDepth(); move_ep.getPostion(); int x = move_ep.getBlankPos_x(), y = move_ep.getBlankPos_y(); move_ep.print(); searched_list.add(move_ep); if (move_ep.isEquals(target_ep)) { System.out.println("终于找到了,⊙﹏⊙b汗"); return; } if (depth < 4) { depth++; EightPuzzle temp = null; temp = move_ep.depthClone(); if (EightPuzzleOperator.canMove(x, y, 1)) { temp = EightPuzzleOperator.movePosition(move_ep, 1); temp.setDepth(depth); if (!searched_list.contains(temp)||(searched_list.contains(temp)&& searched_list.get(searched_list.indexOf(temp)).getDepth()!=temp.getDepth())) { ep_stack.push(temp); } } if (EightPuzzleOperator.canMove(x, y, 2)) { temp = EightPuzzleOperator.movePosition(move_ep, 2); temp.setDepth(depth); if (!searched_list.contains(temp)||(searched_list.contains(temp)&& searched_list.get(searched_list.indexOf(temp)).getDepth()!=temp.getDepth())) { ep_stack.push(temp); } } if (EightPuzzleOperator.canMove(x, y, -1)) { temp = EightPuzzleOperator.movePosition(move_ep, -1); temp.setDepth(depth); if (!searched_list.contains(temp)||(searched_list.contains(temp)&& searched_list.get(searched_list.indexOf(temp)).getDepth()!=temp.getDepth())) { ep_stack.push(temp); } } if (EightPuzzleOperator.canMove(x, y, -2)) { temp = EightPuzzleOperator.movePosition(move_ep, -2); temp.setDepth(depth); if (!searched_list.contains(temp)||(searched_list.contains(temp)&& searched_list.get(searched_list.indexOf(temp)).getDepth()!=temp.getDepth())) { ep_stack.push(temp); } } } } System.out.println("非常遗憾,没有搜索到你需要的目标!%>_<%"); } // 宽度(广度)优先搜索实现 public void breadthFirstSearch() { if (!searched_list.isEmpty()) searched_list.clear(); System.out.println("广度优先搜索方法路径!"); while (!ep_queue.isEmpty()) { EightPuzzle move_ep = ep_queue.poll(); depth = move_ep.getDepth(); move_ep.getPostion(); int x = move_ep.getBlankPos_x(), y = move_ep.getBlankPos_y(); move_ep.print(); searched_list.add(move_ep); if (move_ep.isEquals(target_ep)) { System.out.println("终于找到了,⊙﹏⊙b汗"); return; } depth++; EightPuzzle temp = null; temp = move_ep.depthClone(); if (EightPuzzleOperator.canMove(x, y, 1)) { temp = EightPuzzleOperator.movePosition(move_ep, 1); temp.setDepth(depth); if (!searched_list.contains(temp)) { ep_queue.offer(temp); } } if (EightPuzzleOperator.canMove(x, y, 2)) { temp = EightPuzzleOperator.movePosition(move_ep, 2); temp.setDepth(depth); if (!searched_list.contains(temp)) { ep_queue.offer(temp); } } if (EightPuzzleOperator.canMove(x, y, -1)) { temp = EightPuzzleOperator.movePosition(move_ep, -1); temp.setDepth(depth); if (!searched_list.contains(temp)) { ep_queue.offer(temp); } } if (EightPuzzleOperator.canMove(x, y, -2)) { temp = EightPuzzleOperator.movePosition(move_ep, -2); temp.setDepth(depth); if (!searched_list.contains(temp)) { ep_queue.offer(temp); } } } System.out.println("非常遗憾,没有搜索到你需要的目标!%>_<%"); } /*public void search() { // this.getPostion(); // this.depthFirstSearch(blankPos_x, blankPos_y, 1); this.depthFirstSearch(); this.boundedDepthFirstSearch(); this.breadthFirstSearch(); }*/ /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub EightPuzzleAlgorithm epa = new EightPuzzleAlgorithm(); epa.depthFirstSearch(); epa.boundedDepthFirstSearch(); epa.breadthFirstSearch(); } /* * 测试用例 【1】2 8 3 1 0 4 7 6 5 2 8 3 1 4 0 7 6 5 【2】2 8 3 1 0 4 7 6 5 * */ }