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

  1. 重排九宫问题描述

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

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

      任意棋局对应一个序列 ,其中 为0,1,2…,8九个数中的一个,0表示空格。
    在这里插入图片描述

图2 棋盘对应序列

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

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

    图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所示。
    在这里插入图片描述

图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所示。
    在这里插入图片描述

图5 基于启发式搜索和广度优先搜索的重排九宫算法示例
  1. 两种算法效率比较
      随机选取3组可解的初始状态和目标状态,分别比较在两种算法下生成的搜索树节点的数量,显然节点数量越少,算法效率越高,测试结果如下:
    在这里插入图片描述
      由表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<JiuGongGe> routine = new LinkedList<JiuGongGe>(); 
    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<JiuGongGe> routine = new LinkedList<JiuGongGe>(); 
    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;
    }
}

  • 7
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
重排九宫问题是一种经典的数学难题,也是一种常见的人工智能问题。解决这个问题可以使用宽度优先搜索算法。下面我将介绍如何使用宽度优先搜索算法解决重排九宫问题。 首先,我们将一个 3x3 的棋盘视为一个状态空间,其中每个状态都代表了一个不同的棋局状态。我们的目标是找到一种移动步骤,将当前的棋局状态转换为目标状态。在这个问题中,我们将目标状态设置为如下图所示的状态: ``` 1 2 3 4 5 6 7 8 0 ``` 其中数字 0 代表空格,可以在移动时用于交换两个数的位置。现在我们的任务是找到一种最短的移动步骤,将当前状态转换为目标状态。 接下来,我们将使用宽度优先搜索算法来解决这个问题。我们需要遵循以下步骤: 1. 将当前状态加入队列中,并将其设置为已访问状态。 2. 从队列中取出第一个状态,检查它是否等于目标状态,如果是,则返回移动步骤。 3. 如果不是,我们将生成所有可能的下一步状态,并检查它们是否已经被访问过。如果没有被访问过,则将其加入队列中,并将其设置为已访问状态。 4. 重复步骤 2 和 3,直到找到目标状态或队列为空。 下面是使用 Python 实现宽度优先搜索算法解决重排九宫问题的示例代码: ```python from collections import deque # 目标状态 goal_state = [[1, 2, 3], [4, 5, 6], [7, 8, 0]] # 移动操作 moves = { "up": (-1, 0), "down": (1, 0), "left": (0, -1), "right": (0, 1), } def bfs(start_state): # 初始化队列和已访问状态 queue = deque([(start_state, "", 0)]) visited = set([tuple(map(tuple, start_state))]) # 开始搜索 while queue: state, path, steps = queue.popleft() # 检查是否达到目标状态 if state == goal_state: return path # 生成下一步状态 for move, (dx, dy) in moves.items(): new_state = [row[:] for row in state] x, y = find_zero(state) nx, ny = x + dx, y + dy if 0 <= nx < 3 and 0 <= ny < 3: new_state[x][y], new_state[nx][ny] = new_state[nx][ny], new_state[x][y] if tuple(map(tuple, new_state)) not in visited: queue.append((new_state, path + move, steps + 1)) visited.add(tuple(map(tuple, new_state))) # 无解情况 return "No solution found" def find_zero(state): for i in range(3): for j in range(3): if state[i][j] == 0: return i, j # 测试代码 start_state = [[2, 3, 6], [1, 5, 0], [7, 8, 4]] print(bfs(start_state)) ``` 在上面的代码中,我们使用了 Python 中的 deque 和 set 数据结构来实现队列和已访问状态的功能。我们还定义了一个字典 moves,用于存储每个移动操作对应的行列偏移量。函数 find_zero 用于查找空格的位置。最后,我们使用测试数据调用函数 bfs,并打印出移动步骤。 这就是使用宽度优先搜索算法求解重排九宫问题的方法。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值