重排九宫问题(广度优先搜索、启发式搜索、Java)

  1. 重排九宫问题描述

      重排九宫问题描述如下:用数字1~8标注的棋子摆放在一个3×3共9个格子的棋盘上,空出一个格子使棋子能在盘内水平滑动,8个符号在棋盘上的排列称为8数码的状态,游戏要求给定一个初始的状态和一个终止的状态,且每次只能移动一个棋子,求从任一初始棋局变化到另一目标棋局是否有解,以及有解时的解法[1]。如图1所示。
    重排九宫问题(广度优先搜索、启发式搜索、Java)_第1张图片

图1 8数码谜题
  1. 可解性

      任意棋局对应一个序列 ,其中 为0,1,2…,8九个数中的一个,0表示空格。
    重排九宫问题(广度优先搜索、启发式搜索、Java)_第2张图片

图2 棋盘对应序列

  对于任意N阶棋局,可解性判定归纳如下[2]:
  1)对奇数阶棋盘,只有当初始状态序列与目标状态序列二者的逆序数奇偶性相同时问题有解。
  2)对偶数阶棋盘,只有当初始状态序列与目标状态序列二者各自的逆序列数加其空格所在行数所得结果的奇偶性相同时问题有解。
注:代码中并未使用此方法判断可解性(队列为空时,无解)

  1. 数据结构
      如图3,定义JiuGongGe类来存储表示棋局状态。结合图1,棋局显然可以使用二维数组arr[3][3]存储,若格子内为空,则对应的二维数组元素存储为0;设置整数x、y,来存储空格子的位置;存储父节点,便于回溯,复现求解过程。JiuGongGe(),构造函数,输入二维棋局数组,进行实例化;getXY(),获取空格下标函数;setFather(),设置父节点函数;moveUp(),向上移动函数,判断空格下方是否存在棋子,若存在棋子,棋子向上移动至空格;moveDown(),向下移动函数;moveRight(),向左移动函数;moveLeft(),向右移动函数;equals(),用于判断两种棋局状态是否相同;printArr(),打印当前棋盘状态函数。
    重排九宫问题(广度优先搜索、启发式搜索、Java)_第3张图片

    图3 JiuGongGe类结构

  2. 基于广度优先搜索的重排九宫算法
    Step1:
      输入初始棋盘状态S0和目标棋盘状态Sg对应的二维数组,判断是否可解,若无解转Step8;
    Step2:
      初始化待考察棋盘状态队列q和已考察棋盘状态集合m,并将初始棋盘状态加入队列q;
    Step3:
      队列q出队,将出队棋盘状态n放入集合m;
    Step4:
      考察状态n是否为目标棋盘状态,若是,则求解完成,转Step7;
    Step5:
      考察状态n是否可以扩展,使用moveUp()、moveDown()、moveRight()、moveLeft()函数生成状态,若存在生成的状态未在集合m中,则状态n可扩展,否则,不可扩展。若节点n不可扩展,则转Step3;
    Step6:
      扩展状态n,将其子棋盘状态入队,并为每一个子棋盘状态的父状态都设置为状态n,转Step4;
    Step7:
      使用回溯法,打印求解出的最佳路径;
    Step8:
      结束。
      当初始状态S0=[2 8 3;1 0 4;7 6 5],目标状态Sg=[1 2 3;8 0 4;7 6 5]时,使用广度优先搜算算法形成的搜索树如图4所示。
    重排九宫问题(广度优先搜索、启发式搜索、Java)_第4张图片

图4 基于广度优先搜索的重排九宫算法示例
  1. 基于启发式搜索和广度优先搜索的重排九宫算法
    5.1启发式搜索算法
      盲目搜索具有较大的盲目性,产生的无用节点较多,效率不高。启发式搜索采用问题自身的特性信息,以指导搜索朝着最有希望的方向前进。启发式搜索针对性较强,因而效率较高。
    估价函数
      用于评估节点重要性的函数称为估价函数。其一般形式为:
    在这里插入图片描述

      其中, 表示从初始节点 到节点 的代价;启发函数 是从节点 到目标节点 的最优路径的代价的估计,它体现了问题的启发性信息。
    基于启发式搜索和广度优先搜索的重排九宫算法
    估价函数:
    在这里插入图片描述

      其中, 表示棋盘状态 在搜索树中深度, 表示棋盘状态 与目标棋盘状态 不同的棋子树。
    5.2算法步骤
      具体算法步骤和基于广度优先搜索的重排九宫算法步骤大致相同,只需要将待考察棋盘状态队列q根据估价函数设置为优先队列,估价函数最小的棋盘状态最早出队。
      当初始状态S0=[2 8 3;1 0 4;7 6 5],目标状态Sg=[1 2 3;8 0 4;7 6 5]时,使用启发式搜索和广度优先搜索算法形成的搜索树如图5所示。
    重排九宫问题(广度优先搜索、启发式搜索、Java)_第5张图片

图5 基于启发式搜索和广度优先搜索的重排九宫算法示例
  1. 两种算法效率比较
      随机选取3组可解的初始状态和目标状态,分别比较在两种算法下生成的搜索树节点的数量,显然节点数量越少,算法效率越高,测试结果如下:
    重排九宫问题(广度优先搜索、启发式搜索、Java)_第6张图片
      由表1可知,基于启发式搜索和广度优先搜索的重排九宫算法大大提高了求解效率。
参考文献

[1]. 毕智超.基于重排九宫问题的算法设计与实现.技术与市场学报,2017,24(9):53-54.
[2]. 陈一如.重排九宫中的可解性定理.上海海运学院学报,1988,(3):71-73.

Java源代码
  1. 基于广度优先搜索的重排九宫算法
import java.util.HashSet;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Scanner;
import java.util.Set;

public class JiuGong2{
    private int[][] start;
    private int[][] end;
    private JiuGongGe NjiuGongGe;
    private JiuGongGe EjiuGongGe;
    private int efficency;
    private Queue<JiuGongGe> q = new PriorityQueue<JiuGongGe>(); 
    //private Queue routine = new LinkedList(); 
    private Set<JiuGongGe> m = new HashSet<JiuGongGe>();

    JiuGong2(int [][] start,int[][] end){
        this.start=start;
        this.end=end;
        NjiuGongGe=new JiuGongGe(this.start);
        NjiuGongGe.setDiff(end);
        NjiuGongGe.setW();
        EjiuGongGe=new JiuGongGe(this.end);
        efficency=1;
        
    }
    public void move(){
        JiuGongGe temp;
        temp=NjiuGongGe.moveUp();
        
        if(temp!=null&&(!m.contains(temp))){
            temp.setFather(NjiuGongGe);
            temp.setDiff(end);
            temp.setLen(NjiuGongGe.getLen()+1);
            temp.setW();
            q.offer(temp);
            efficency++;
        }
        temp=NjiuGongGe.moveDown();
        
        if(temp!=null&&(!m.contains(temp))){
            temp.setFather(NjiuGongGe);
            temp.setDiff(end);
            temp.setLen(NjiuGongGe.getLen()+1);
            temp.setW();
            q.offer(temp);
            efficency++;
        }
        temp=NjiuGongGe.moveLeft();
        
        if(temp!=null&&(!m.contains(temp))){
            temp.setFather(NjiuGongGe);
            temp.setDiff(end);
            temp.setLen(NjiuGongGe.getLen()+1);
            temp.setW();
            q.offer(temp);
            efficency++;
        }
        temp=NjiuGongGe.moveRight();
        
        if(temp!=null&&(!m.contains(temp))){
            temp.setFather(NjiuGongGe);
            temp.setDiff(end);
            temp.setLen(NjiuGongGe.getLen()+1);
            temp.setW();
            q.offer(temp);
            efficency++;
        }
    }
    public int getEfficiency(){
        return efficency;
    }
    public boolean getRoutine(){
        q.offer(NjiuGongGe);
        
            while(!q.isEmpty()){
                
                if(NjiuGongGe.equals(EjiuGongGe)){
                    return true;
                }else{
                    NjiuGongGe=q.poll();
                    m.add(NjiuGongGe);
                    move();
                }
                //System.out.println(q.size());
                //System.out.println(m.size());
                
            }
            return false;
    }
    public void printRoutine(){
        while(NjiuGongGe.getFather()!=null){
            NjiuGongGe.printArr();
            System.out.println();
            NjiuGongGe=NjiuGongGe.getFather();
        }
        for(int i=0;i<start.length;i++){
            for(int j=0;j<start.length;j++){
                System.out.print(start[i][j]);
                System.out.print(" ");
            }
            System.out.println();
        }
    }
    public static void main(String[] args) {
        //int [][]start={{2,8,3},{1,0,4},{7,6,5}};
        //int [][]end={{1,2,3},{8,0,4},{7,6,5}};
        int start[][];
        int end[][];
        start=new int[3][3];
        end=new int[3][3];
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入初始状态");
        for(int i=0;i<3;i++){
            for (int j=0;j<3;j++){
                start[i][j]=sc.nextInt();
                
            }
        }
        System.out.println("请输入目标状态");
        for(int i=0;i<3;i++){
            for (int j=0;j<3;j++){
                end[i][j]=sc.nextInt();
                
            }
        }
        sc.close();
        JiuGong2 t=new JiuGong2(start,end);
        if(t.getRoutine()){
            t.printRoutine();
            System.out.println("搜索树节点数量:"+t.getEfficiency());
        }else{
            System.out.println("无解");
        }
        
    }
    
}
*********************************************************
import java.util.Arrays;
public class JiuGongGe implements Comparable<JiuGongGe>{
    private int[][] arr;
    private int x;
    private int y;
    private int len;
    private int diff;
    private int w;
    private JiuGongGe father;
    JiuGongGe(int[][] arr){
        this.arr=arr;
        getXY();
        father=null;
        len=0;
        diff=0;
    }

    private void getXY() {
        for(int i=0;i<arr.length;i++){
            for (int j=0;j<arr.length;j++){
                if(arr[i][j]==0){
                    x=i;
                    y=j;
                }
            }
        }
    }
    public void setW(){
        w=len+diff;
    }
    public void setLen(int len){
        this.len=len;
    }
    public int getLen(){
        return len;
    }
    public void setDiff(int[][] end){
        diff=0;
        for(int i=0;i<end.length;i++){
            for (int j=0;j<end.length;j++){
                if(arr[i][j]!=end[i][j]){
                    diff++;
                }
            }
        }
    }
    public void setFather(JiuGongGe F){
        this.father=F;
    }
    public JiuGongGe getFather(){
        return this.father;
    }
    public void printArr(){
        for(int i=0;i<arr.length;i++){
            for(int j=0;j<arr.length;j++){
                System.out.print(arr[i][j]);
                System.out.print(" ");
            }
            System.out.println();
        }
    }
    JiuGongGe moveUp(){
        int tempx=x-1;
        
        int[][] tempArr=new int[3][3];
        for (int i=0;i<tempArr.length;i++){
            tempArr[i]=arr[i].clone();
        }
        if(tempx>=0){
            tempArr[x][y]=tempArr[tempx][y];
            tempArr[tempx][y]=0;
            return new JiuGongGe(tempArr);
        }else{
            return null;
        }
    }
    JiuGongGe moveDown(){
        int tempx=x+1;
        int[][] tempArr=new int[3][3];
        for (int i=0;i<tempArr.length;i++){
            tempArr[i]=arr[i].clone();
        }
        if(tempx<=2){
            tempArr[x][y]=tempArr[tempx][y];
            tempArr[tempx][y]=0;
            return new JiuGongGe(tempArr);
        }else{
            return null;
        }
    }
    JiuGongGe moveLeft(){
        int tempy=y-1;
        int[][] tempArr=new int[3][3];
        for (int i=0;i<tempArr.length;i++){
            tempArr[i]=arr[i].clone();
        }
        if(tempy>=0){
            tempArr[x][y]=tempArr[x][tempy];
            tempArr[x][tempy]=0;
            return new JiuGongGe(tempArr);
        }else{
            return null;
        }
    }

    JiuGongGe moveRight(){
        int tempy=y+1;
        int[][] tempArr=new int[3][3];
        for (int i=0;i<tempArr.length;i++){
            tempArr[i]=arr[i].clone();
        }
        if(tempy<=2){
            tempArr[x][y]=tempArr[x][tempy];
            tempArr[x][tempy]=0;
            return new JiuGongGe(tempArr);
        }else{
            return null;
        }
    }

    public boolean equals(Object o){
        if (this == o) {
            return true;
        }else{
            if (o!=null && o instanceof JiuGongGe){
                JiuGongGe j=(JiuGongGe)o;
                if(Arrays.equals(arr[0], j.arr[0])&&Arrays.equals(arr[1], j.arr[1])&&Arrays.equals(arr[2], j.arr[2])){
                    return true;
                }else{
                    return false;
                }
            }
        }
        return false;
    }
    @Override
    public int compareTo(JiuGongGe o) {
        
        if(this.w==o.w){
            return 0;
        }else if(this.w>o.w){
            return 1;
        }else {
            return -1;
        }

    }
}
  1. 基于启发式搜索和广度优先搜索的重排九宫算法
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Set;
import java.util.Scanner;

public class JiuGong2{
    private int[][] start;
    private int[][] end;
    private JiuGongGe NjiuGongGe;
    private JiuGongGe EjiuGongGe;
    private int effiency;
    private Queue<JiuGongGe> q = new LinkedList<JiuGongGe>(); 
    //private Queue routine = new LinkedList(); 
    private Set<JiuGongGe> m = new HashSet<JiuGongGe>();

    JiuGong2(int [][] start,int[][] end){
        this.start=start;
        this.end=end;
        NjiuGongGe=new JiuGongGe(this.start);
        EjiuGongGe=new JiuGongGe(this.end);
        effiency=1;
    }
    public void move(){
        JiuGongGe temp;
        temp=NjiuGongGe.moveUp();
        
        if(temp!=null&&(!m.contains(temp))){
            temp.setFather(NjiuGongGe);
            q.offer(temp);
            effiency++;
        }
        temp=NjiuGongGe.moveDown();
        
        if(temp!=null&&(!m.contains(temp))){
            temp.setFather(NjiuGongGe);
            q.offer(temp);
            effiency++;
        }
        temp=NjiuGongGe.moveLeft();
        
        if(temp!=null&&(!m.contains(temp))){
            temp.setFather(NjiuGongGe);
            q.offer(temp);
            effiency++;
        }
        temp=NjiuGongGe.moveRight();
        
        if(temp!=null&&(!m.contains(temp))){
            temp.setFather(NjiuGongGe);
            q.offer(temp);
            effiency++;
        }
    }
    public int getEffiency(){
        return effiency;
    }
    public boolean getRoutine(){
        q.offer(NjiuGongGe);
        
            while(!q.isEmpty()){
                if(NjiuGongGe.equals(EjiuGongGe)){
                    return true;
                }else{
                    m.add(NjiuGongGe);
                    move();
                    NjiuGongGe=q.poll();
                }
                
            }
            return false;
    }
    public void printRoutine(){
        while(NjiuGongGe.getFather()!=null){
            NjiuGongGe.printArr();
            System.out.println();
            NjiuGongGe=NjiuGongGe.getFather();
        }
        for(int i=0;i<start.length;i++){
            for(int j=0;j<start.length;j++){
                System.out.print(start[i][j]);
                System.out.print(" ");
            }
            System.out.println();
        }
    }
    public static void main(String[] args) {
        //int [][]start={{2,8,3},{1,0,4},{7,6,5}};
        //int [][]end={{1,2,3},{8,0,4},{7,6,5}};
        int start[][];
        int end[][];
        start=new int[3][3];
        end=new int[3][3];
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入初始状态");
        for(int i=0;i<3;i++){
            for (int j=0;j<3;j++){
                start[i][j]=sc.nextInt();
                
            }
        }
        System.out.println("请输入目标状态");
        for(int i=0;i<3;i++){
            for (int j=0;j<3;j++){
                end[i][j]=sc.nextInt();
                
            }
        }
        sc.close();
        JiuGong2 t=new JiuGong2(start,end);
        if(t.getRoutine()){
            t.printRoutine();
            System.out.println("搜索树节点数量:"+t.getEffiency());
        }else{
            System.out.println("无解");
        }
    }
    
}

*********************************************************
import java.util.Arrays;
public class JiuGongGe{
    private int[][] arr;
    private int x;
    private int y;
    private JiuGongGe father;
    JiuGongGe(int[][] arr){
        this.arr=arr;
        getXY();
        father=null;
    }
    private void getXY() {
        for(int i=0;i<arr.length;i++){
            for (int j=0;j<arr.length;j++){
                if(arr[i][j]==0){
                    x=i;
                    y=j;
                }
            }
        }
    }
    public void setFather(JiuGongGe F){
        this.father=F;
    }
    public JiuGongGe getFather(){
        return this.father;
    }
    public void printArr(){
        for(int i=0;i<arr.length;i++){
            for(int j=0;j<arr.length;j++){
                System.out.print(arr[i][j]);
                System.out.print(" ");
            }
            System.out.println();
        }
    }
    JiuGongGe moveUp(){
        int tempx=x-1;
        
        int[][] tempArr=new int[3][3];
        for (int i=0;i<tempArr.length;i++){
            tempArr[i]=arr[i].clone();
        }
        if(tempx>=0){
            tempArr[x][y]=tempArr[tempx][y];
            tempArr[tempx][y]=0;
            return new JiuGongGe(tempArr);
        }else{
            return null;
        }
    }
    JiuGongGe moveDown(){
        int tempx=x+1;
        int[][] tempArr=new int[3][3];
        for (int i=0;i<tempArr.length;i++){
            tempArr[i]=arr[i].clone();
        }
        if(tempx<=2){
            tempArr[x][y]=tempArr[tempx][y];
            tempArr[tempx][y]=0;
            return new JiuGongGe(tempArr);
        }else{
            return null;
        }
    }
    JiuGongGe moveLeft(){
        int tempy=y-1;
        int[][] tempArr=new int[3][3];
        for (int i=0;i<tempArr.length;i++){
            tempArr[i]=arr[i].clone();
        }
        if(tempy>=0){
            tempArr[x][y]=tempArr[x][tempy];
            tempArr[x][tempy]=0;
            return new JiuGongGe(tempArr);
        }else{
            return null;
        }
    }

    JiuGongGe moveRight(){
        int tempy=y+1;
        int[][] tempArr=new int[3][3];
        for (int i=0;i<tempArr.length;i++){
            tempArr[i]=arr[i].clone();
        }
        if(tempy<=2){
            tempArr[x][y]=tempArr[x][tempy];
            tempArr[x][tempy]=0;
            return new JiuGongGe(tempArr);
        }else{
            return null;
        }
    }

    public boolean equals(Object o){
        if (this == o) {
            return true;
        }else{
            if (o!=null && o instanceof JiuGongGe){
                JiuGongGe j=(JiuGongGe)o;
                if(Arrays.equals(arr[0], j.arr[0])&&Arrays.equals(arr[1], j.arr[1])&&Arrays.equals(arr[2], j.arr[2])){
                    return true;
                }else{
                    return false;
                }
            }
        }
        return false;
    }
}

你可能感兴趣的:(重排九宫问题(广度优先搜索、启发式搜索、Java))