狀態空間搜尋——八數碼問題 Java實現
狀態空間搜尋——八數碼問題
實驗報告
【注】原始碼將以附件的形式上傳,其中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
*
*/
}