隐式图的搜索问题(准备+结果)


一、问题的概述、分析

1.问题的概述

1)实验任务:

  • 对九宫重排问题,建立图的启发式搜索求解方法;
  • 用A*算法求解九宫重排问题。

2)实验要求:

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

2.问题的分析

1)A*算法

F=G+H
G:表示从起点A移动到指定方格的移动消耗;
H:表示从指定的方格移动到目标E点的预计消耗,我们假设H的计算方法,忽略障碍物,只可以纵横向计算。

2)九宫格的重新排布

每次交换后都要改变棋盘的布局,使九宫格从初始状态变换为最终状态,空格可与其周围上下左右的数字交换。

二、开发工具及编程语言

1.开发工具

IntelliJ IDEA

2.编程语言

Java语言

三、算法分析(A*算法)

A*算法

求解一点到另一点的最优路径。将地图虚拟化,并将其划分为一个一个的小方块,然后不断地寻找周围的点,选出一个新的点作为起点后再循环找点,直至找到终点。

路径增量

  • F=G+H
  • G:表示从起点A移动到指定方格的移动消耗,我们假设横向移动一个格子消耗10,斜向移动一个格子消耗14;
  • H:表示从指定的方格移动到目标E点的预计消耗。

流程描述

  • 从起点s开始,把s作为一个等待检查的方格,放入到“开启列表”中;
  • 寻找起点s周围可以到达的方格(最多八个),将它们放入到“开启列表”,并设置它们的父方格为s;
  • 从“开启列表”中删除起点s,并将s放入到“关闭列表”中;
  • 计算每个周围方格的F值,F=G+H

好物分享

参考文献1
参考文献2
参考文献3

三、算法分析

Node节点

public class Node {
    //用三维数组来存储九宫格当前的状态
    int [][] currentState = new int[3][3];
    //将初始位置的父状态设置为空
    Node DadState =null;
    //将四个方向变化后的状态称为四个孩子状态
    Node child1 = null , child2 = null , child3 = null , child4=null;
    //评估价值的函数 f = g + h ,  h为变化层数,假设初始状态为第0层。下一状态为第1层
    private int f;
    int h=0;

    /**
     * 带参数的构造函数
     * @param States
     * @param dad
     * @param child1
     * @param child2
     * @param child3
     * @param child4
     * @param h
     */
    public Node(int [][]States,Node dad,Node child1,Node child2,Node child3,Node child4,int h){

        //传入外界数组
        for(int i=0;i<3;i++){
            for(int j=0;j<3;j++) {
                currentState[i][j] = States[i][j];
            }
        }

        //传入孩子状态
        this.child1 =child1;
        this.child2 =child2;
        this.child3 =child3;
        this.child4 =child4;

        //传入变化层数
        this.h=h;
    }

    public int getF(){
        return f;
    }

    public void setF(int g) {
        f=g+h;
    }
}

功能模块类

mport java.util.ArrayList;
import java.util.List;
import java.util.Scanner;

public class ImplicitService {
    Node Begin;
    Node End;

    //open链表存储空格移动后的新状态
    List<Node> open = new ArrayList<>();
    //close链表存储已经确定了的状态
    List<Node> close = new ArrayList<>();
    Scanner scanner=new Scanner(System.in);

    /**
     * 构造函数
     */
    public ImplicitService(){
        int [][]beginState=new int[3][3];
        int [][]purposeState=new int[3][3];

        //起始位置
        System.out.println("起始位置:");
        for(int i=0;i<3;i++) {
            for (int j = 0; j < 3; j++) {
                System.out.println("请输入你的数字:");
                beginState[i][j] = scanner.nextInt();
            }
        }

        //存入初始状态
        Begin = new Node(beginState,null,null,null,null,null,0);

        //目标位置
        System.out.println("目标位置:");
        for(int i=0;i<3;i++) {
            for (int j = 0; j < 3; j++) {
                System.out.println("请输入你的数字:");
                purposeState[i][j] = scanner.nextInt();
            }
        }

        //存入结果状态
        End =new Node(purposeState,null,null,null,null,null,0);
    }

    /**
     * 程序执行开关
     */
    public void start () {
        //判断题目是否有答案
        if (judgeIfResult(Begin, End)) {
            Node replace = Begin;
            //将初始态加入到Open链表
            open.add(replace);
            //判断是否初始态与结果相同,如果不同
            while (!ifFinish(replace, End)) {
                //删除该状态
                open.remove(0);
                //对初始状态运行空格移动的方法
                addState(replace);
                //之后对open链表进行排序
                openSort();
                //将open链表里f值最小的一个赋值给replace
                replace = open.get(0);
            }
            //将replace添加到close链表中
            Node willAddClose = replace;
            while (willAddClose != null) {
                close.add(willAddClose);
                willAddClose = willAddClose.DadState;
            }
            //打印输出
            print();
        } else {
            System.out.println("没有结果!");
        }
    }

    /**
     * 判断题目是否有答案
     * @param begin
     * @param end
     * @return
     */
    public boolean judgeIfResult(Node begin, Node end){
        //用一维数组开始记录展开
        int []oneArray1 = new int[9];
        int []oneArray2 = new int[9];
        int inverseNumber1  = 0,  inverseNumber2  = 0 ;
        int count1 = 0,  count2 = 0;

        //二维数组展开
        int i,j;
        for(i=0;i<3;i++) {
            for(j=0;j<3;j++){
                oneArray1[count1++] = begin.currentState[i][j];
                oneArray2[count2++] = end.currentState[i][j];
            }
        }

        //找第一个状态的逆序数
        for(i=0;i<9;i++){
            for(j=0;j<i;j++) {
                if (oneArray1[j] > oneArray1[i] && oneArray1[j] != 0 && oneArray1[i] != 0)
                    inverseNumber1++;
            }
        }

        //找第二个状态的逆序数
        for(i=0;i<9;i++){
            for(j=0;j<i;j++) {
                if (oneArray2[j] > oneArray2[i] && oneArray2[j] != 0 && oneArray2[i] != 0)
                    inverseNumber2++;
            }
        }

        //判断两个逆序数的奇数偶性是否相等
        if(inverseNumber1 %2 == inverseNumber2 %2)
            return true;
        else
            return false;
    }

    /**
     * 判断是不是已经完成了题目的要求
     * @param begin
     * @param end
     * @return
     */
    public boolean ifFinish (Node begin, Node end) {
        //判断两个二维数组是否一样
        for(int i=0;i<3;i++) {
            for(int j=0;j<3;j++){
                if(begin.currentState[i][j] != end.currentState[i][j])
                    return false;
            }
        }
        return true;
    }

    /**
     *打印显示移动步骤
     */
    public void print(){
        System.out.println("移动步骤如下:");
        for(int count = close.size()-1; count >= 0 ; count--) {
            System.out.println("第" + (close.size()-count) + "步:");
            //输出当前已经确定的close链表里的状态的二维数组
            for(int i=0;i<3;i++) {
                for(int j=0;j<3;j++){
                    System.out.print(close.get(count).currentState[i][j]+" ");
                }
                System.out.println();
            }
        }
    }

    /**
     *添加空格移动四个方向之后的四个状态
     * @param parent
     */
    public void addState( Node parent ){
        //存放空格位置
        int count;
        //移动后的新状态
        Node newState;
        //定义四个二维数组用来存储
        int [][]twoArray1 = new int[3][3];
        int [][]twoArray2 = new int[3][3];
        int [][]twoArray3 = new int[3][3];
        int [][]twoArray4 = new int[3][3];

        //把当前状态的二维数组赋值给新定义的四个数组
        for(int i=0;i<3;i++) {
            for(int j=0;j<3;j++){
                twoArray1[i][j]=parent.currentState[i][j];
                twoArray2[i][j]=parent.currentState[i][j];
                twoArray3[i][j]=parent.currentState[i][j];
                twoArray4[i][j]=parent.currentState[i][j];
            }
        }

        //寻找空格位置
        int x=0,y=0;
        for(int i=0;i<3;i++) {
            for(int j=0;j<3;j++){
                if(twoArray1[i][j]==0) {
                    x=i;
                    y=j;
                }
            }
        }

        //判断空格是否能够上移
        if ( x-1 >= 0){
            //空格赋值
            count = twoArray1[x][y];
            //空格上边的值换到空格位置
            twoArray1[x][y]=twoArray1[x-1][y];
            //空格放到空格上边的位置
            twoArray1[x-1][y]=count;
            //新状态定义
            newState = new Node(twoArray1,null,null,null,null,null,0);

            //判断移动后的状态是否为要求的最终状态
            if (!compare(Begin, newState)){
                //设置当前状态为父状态的第一个子状态
                parent.child1 = newState;
                //设置当前状态的父状态
                newState.DadState = parent;
                //层数+1
                newState.h = parent.h + 1;
                //计算g值,即当前状态与最终状态不一样的值的个数
                int count2 = differentNum(newState, End);
                //设置当前状态的f值
                newState.setF(count2);
                //将新状态添加到open链表
                open.add(newState);
            }
        }

        //判断空格是否能够下移
        if ( x+1 <= 2) {
            count=twoArray2[x][y];
            twoArray2[x][y]=twoArray2[x+1][y];
            twoArray2[x+1][y]=count;
            newState = new Node(twoArray2,null,null,null,null,null,0);
            if (!compare(Begin, newState)) {
                parent.child2=newState;newState.DadState=parent;
                newState.h=parent.h+1;
                int count2 =differentNum(newState, End);
                newState.setF(count2);
                open.add(newState);
            }
        }

        //判断空格能不能左移
        if ( y-1 >= 0) {
            count=twoArray3[x][y];
            twoArray3[x][y]=twoArray3[x][y-1];
            twoArray3[x][y-1]=count;
            newState = new Node(twoArray3,null,null,null,null,null,0);
            if (!compare(Begin, newState)) {
                parent.child3=newState;newState.DadState=parent;
                newState.h=parent.h+1;
                int count2=differentNum(newState, End);
                newState.setF(count2);
                open.add(newState);
            }
        }

        //判断空格能不能右移
        if ( y+1 <= 2) {
            count=twoArray4[x][y];
            twoArray4[x][y]=twoArray4[x][y+1];
            twoArray4[x][y+1]=count;
            newState = new Node(twoArray4,null,null,null,null,null,0);
            if (!compare(Begin, newState)) {
                parent.child4=newState;newState.DadState=parent;
                newState.h=parent.h+1;
                int count2=differentNum(newState, End);
                newState.setF(count2);
                open.add(newState);
            }
        }
    }

    /**
     * 比较是否完成运算
     * @param begin
     * @param newState
     * @return
     */
    public boolean compare(Node begin,Node newState){
        //判断是否完成
        if (begin != null && ifFinish(begin,newState))
            return true;
        if (begin.child1 != null)
            compare(begin.child1, newState);
        if (begin.child2 != null)
            compare(begin.child2, newState);
        if (begin.child3 != null)
            compare(begin.child3, newState);
        if (begin.child4 != null)
            compare(begin.child4, newState);
        return false;
    }

    /**
     * 计算g值,即当前状态与最终状态不一样的值的个数
     * @param first
     * @param second
     * @return
     */
    public int differentNum(Node first,Node second) {
        int count = 0;
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                if (first.currentState[i][j] != second.currentState[i][j])
                    count++;
            }
        }
        return count;
    }

    /**
     *寻找open链表里面状态F值最小的一个,然后重新排列open,双向冒泡排序
     */
    public void openSort(){
        //替换节点
        Node node;
        int left=0,right= open.size()-1;
        while(left<right) {
            for(int i=left+1 ; i <= right ; i++){
                if(open.get( left).getF() > open.get(i).getF() ){
                    node = open.get(i);
                    open.set(i, open.get(left));
                    open.set(left,node);
                }
            }
            left++;

            for(int i=right-1;i >= left ; i--){
                if(open.get(right).getF() < open.get(i).getF()){
                    node= open.get(i);
                    open.set(i, open.get(right));
                    open.set(right,node);
                }
            }
            right--;
        }
    }
}

Main函数

public class Main {
    public static void main(String[] args) {
        ImplicitService implicitService=new ImplicitService();
        implicitService.start();
    }
}

四、总结

  1. A*算法算和九宫格的重新排布都算是全新的知识点,导致最初在编写时无从下手,也毫无头绪,但所幸在大量的资料阅读下,最终勉强吧功能实现了,但是可以看出优化的点非常多;
  2. 在查阅资料的途中,由于资料的杂乱性花费了很多无用功,还是在同学和学长的推荐下看了一些比较好的资料,但在看过资料、对知识点不完全理解的情况下,思维很容易遇到瓶颈,所以平时还是要多阅读一些跟算法有关的材料。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值