여덟 숫자, Jiehua Rongdao (bfs, 글로벌 최초, A* 검색)

【문제 설명】

주제 6: 숫자 배열 문제

        각 위치는 3개의 행과 3개의 열로 구성된 숫자의 정사각 행렬입니다. 각 위치는 0부터 8까지의 숫자이며 서로 다릅니다. 초기 위치(직접 설정)에서 다음 위치로 이동하는 "가장 빠른" 방법을 찾으세요. 터미널 위치(스스로 설정).

이동규칙 : 상하좌우 4방향으로 한 번에 0과 인접한 숫자로 교환 가능! 해결책이 있을 수도 있고, 해결책이 없을 수도 있습니다.

0

1

2

 

?

 

1

2

4

5

 

=》

 

4

5

6

6

7

8

 

 

 

7

8

0

 

【수요분석】

1. 기본 기능

1. 사용자는 초기 인터페이스와 종료 인터페이스를 직접 설정하고 9각형 그리드의 사각형을 이동할 수 있습니다.

2. 초기 인터페이스와 종료 인터페이스를 기준으로 이동경로를 출력합니다.

 

2. 추가 기능

1. 9각형 그리드의 사용자 이동에 대한 롤백 기능;

2. 이동 경로를 검색하기 위해 A* 검색, 전역 우선, 너비 우선의 세 가지 알고리즘이 사용됩니다.

3. A* 검색 및 글로벌 우선 선택 알고리즘을 위해 네 가지 평가 함수가 설계되었습니다.

4. 평가 함수 및 알고리즘 지정 시 이동 단계 수와 시간을 결정하고 결과를 데이터베이스에 저장한 후 프런트엔드에 반영합니다.

 

【개요 디자인】

1. 시스템 정보

개발 하드웨어 환경

        CPU: 11세대 Intel(R) Core(TM) i7-11800H @ 2.30GHz 2.30GHz

        RAM: 16.0GB

시스템에 맞는 운영체제 개발

        버전: Windows 10 Home 중국어 버전

소프트웨어 개발 환경/개발 도구

        Intellij IDEA2021.1.1

        비주얼 스튜디오 코드

        HBuilder X

         우편배달부、ApiPost7

프로그래밍 언어

        자바

기술적인 포인트

        백엔드 기술: Springboot, SpringMVC, Knife4j, Mybatis-plus, MySQL, maven, lombok

        프런트엔드 기술: HTML, CSS, JavaScript, Vue, JQuery, Element-ui, Layui, axios

 

2. 기능 모듈

        필요에 따라 설계된 8자리 해결 시스템의 기능에는 9제곱 그리드 이동, 이동 경로 검색 및 상태 기록의 세 가지 범주가 포함됩니다. 자세한 내용은 그림 1에 나와 있습니다.

47958e9bdefb4c54af2d575510059efa.png

 

그림 1 시스템 기능 다이어그램

 

3. 프로젝트 구조

(1) MVC 3계층 아키텍처

표 1 MVC 아키텍처 테이블

수준

효과

설계 원칙

모델 레이어(MODEL)

일련의 애플리케이션 데이터를 캡슐화하고 이러한 데이터를 처리하기 위한 작업, 논리 및 계산 규칙을 ​​정의합니다.

인터페이스를 호출하여 컨트롤러에 피드백

뷰 레이어

(보다)

뷰 객체의 주요 목적 중 하나는 애플리케이션 모델 객체의 데이터를 표시하고 사용자가 해당 데이터를 편집할 수 있도록 하는 것입니다.

ajax를 통해 비동기 요청을 보내고 컨트롤러에 피드백을 제공합니다.

컨트롤러 레이어(컨트롤러)

컨트롤러는 뷰 레이어와 여러 모델 레이어 사이의 중개자입니다.

모델 레이어와 뷰 레이어를 직접 운영

 

(2) 패키지 구조

        프로젝트는 기본 MVC 3계층 아키텍처를 사용합니다. 여기서 공통 엔터티 클래스는 공통 패키지 아래에 저장되고, Tomcat 서버에 대한 구성 정보는 config 아래에 저장되며, 제어 계층은 컨트롤러 패키지 아래에 있고, 데이터베이스 작업을 위한 인터페이스 인터페이스는 다음과 같습니다. 매퍼 계층 아래에는 서비스가 비즈니스 계층이고, 시스템 아래에는 엔터티 클래스가 있으며, vo 패키지 아래에 정의된 클래스는 데이터와 정보를 전송하는 데 사용되는 값 개체입니다. 패키지 디렉터리 구조는 그림 2에 나와 있습니다.

eb2247fcde20493abf7881cb4bc56dda.png

 

그림 2 프로젝트 패키지 디렉터리

 

4. 저장구조

        JavaEE 개발 및 설계 과정에서 C 언어의 구조와 유사하게 Java의 저장소는 엔터티 클래스에 배치됩니다. 예를 들어 Node 노드와 Algorithm 노드에 세 가지 알고리즘과 관련된 정보를 저장하는데, Node 노드의 저장 구조는 그림 9와 같다.

표 2 노드의 저장 구조 Node

데이터 항목 이름

데이터 항목 시스템 표현

데이터 유형

데이터 길이

주목

정점 데이터 저장

숫자

정수[][]

3*3

 

이동 단계/깊이

깊이

정수

 

 

역서수

역순_번호

정수

 

 

상위 노드 이동 방향

방향

정수

 

 

평가함수 가치

가치

정수

 

 

        Node 클래스의 코드는 다음과 같습니다.

public class Node {



public int[][] num =new int [3][3]; //用于存放节点数据



public int depth; //移动步数/深度



int Reverse_order_number; //逆序数



public int direction;//1 2 3 4 分别为上下左右



public int worth; //启发式函数的值

}

        노드 알고리즘의 저장 구조는 표 3에 나와 있습니다.

표 3 알고리즘 저장 구조

데이터 항목 이름

데이터 항목 시스템 표현

데이터 유형

데이터 길이

주목

num의 초기 상태

num_초기

정수[][]

3*3

 

num의 최종 상태

num_최종

정수[][]

3*3

 

시작 시간

별시간

 

 

최대 검색 길이

MAX_SEARCH_DEPTH

정수

 

 

        알고리즘 클래스의 소스 코드는 다음과 같습니다.

public class Algorithm {



    public static int[][] num_Initial =new int [3][3];



    public static int[][] num_Eventual =new int [3][3];



    public static long starTime;



    public static int MAX_SEARCH_DEPTH=100;

}

        함수 실행 결과의 저장 클래스는 StepAndTime 이며, 저장 구조는 다음과 같습니다.

표 4 StepAndTime 저장 구조

데이터 항목 이름

데이터 항목 시스템 표현

데이터 유형

데이터 길이

주목

기본 키

ID

 

 

알고리즘 이름

알고리즘_이름

 

 

평가함수

알고리즘_값_추정

 

 

보낸 시간

시간_비용

 

 

걸음수

단계

정수

 

 

        StepAndTime 클래스의 코드는 다음과 같습니다.

public class StepAndTime {



    private String id;



    private String algorithm_name;



    private String algorithm_value_estimate;



    private String time_cost;



    private int step;

}

        여기서 StepAndTime 노드는 데이터베이스에 저장되어 데이터베이스의 추가, 삭제, 수정 및 쿼리 기능을 실현할 수 있습니다. step_and_time 테이블의 구조는 그림 3에 나와 있습니다.

 

0efcbdb4df0644ba86fc6b81795676ea.png

 그림 3 데이터베이스 스토리지

 

【세부 디자인】

1. 구궁격자의 움직임

(1) 9각형 그리드 이동

        두 개의 9각형 그리드가 프런트 엔드 인터페이스에 배치됩니다. 각 9각형 그리드에는 1부터 8까지의 8개의 숫자가 포함됩니다. 첫 번째 9각형 그리드는 초기 상태를 나타내고 두 번째 9각형 그리드는 대상 상태를 나타냅니다. 사용자가 빈 블록, 즉 9개의 정사각형 그리드를 클릭하면 숫자가 포함된 8개의 블록이 아닌 다른 블록이 있을 경우 해당 블록은 빈 블록으로 교환되며, 교환 로직은 그림 4와 같다.

3b232c247cb74b38b14a834707c0e05a.png

그림 4 사각형 클릭에 따른 이동 효과

(2) 주공게 현황 및 이동현황을 기록한다.

        ti2 배열을 사용하여 Jiugongge의 상태를 기록하고 문자열 연결을 사용하여 Jiugongge 이동 상태(롤백용)를 기록하고 콘솔에 내용을 인쇄합니다.

e9fe3e18f95b4e4ebb6e2b856230e698.png

 

 

그림 5 9각형 그리드의 상태 기록

(3) 롤백 기능

        이전 9각형 그리드 상태로 돌아가려면 아래의 뒤로 버튼을 클릭하세요.

 

2. 검색 경로

(1) 전역 최적화 알고리즘

         전역 우선 검색은 무차별 대입 및 철저한 맹검 검색이 아닌 알려진 문제의 휴리스틱 정보를 사용하여 문제 해결을 안내하는 휴리스틱 검색입니다.

휴리스틱 정보 : 즉, 검색 프로세스를 안내하는 데 사용할 수 있고 특정 문제 해결과 관련된 제어 정보입니다.

휴리스틱 함수(Heuristic function) : 휴리스틱 정보를 기술하기 위해 사용되는 수학적 모델을 휴리스틱 함수라고 하며, 문제의 특성과 문제를 바라보는 관점에 따라 동일한 문제에 대해 다수의 휴리스틱 함수를 정의할 수 있다.

 

        전역 최적화 알고리즘의 실행 과정은 먼저 초기 노드 S0를 OPEN 테이블에 넣고, 현재 노드의 깊이와 현재 노드와 노드 간의 차이 측정을 기반으로 휴리스틱 함수 f(S0)를 계산하는 것입니다. 대상 노드입니다. OPEN 테이블이 비어 있으면 문제가 해결되지 않은 것이므로 프로그램을 종료하십시오. OPEN 테이블이 아직 비어 있지 않으면 OPNE 테이블의 첫 번째 노드 n을 CLOSED 테이블에 넣고 노드가 있는지 확인하십시오. 가 대상 노드라면 OK 문제를 직접 해결하고 프로그램을 종료합니다. 대상 노드를 찾지 못한 경우 노드 n을 확장할 수 없습니다. Step 2로 이동합니다. 노드 n을 확장할 수 있으면 , 노드를 확장하고 평가 함수 f(n)을 사용하여 각 노드의 추정 값을 계산하고 각 자식 노드에 대해 부모 노드에 대한 포인터를 구성하고 이러한 자식 노드를 OPEN 테이블에 넣은 다음 모든 노드를 정렬합니다. 평가함수 f(n)의 크기에 따라 OPEN 테이블의 노드를 선택하고 Step2로 진행하며, 전역최적 알고리즘의 실행과정은 표 5와 같다.

표 5 전역 최적화 알고리즘 실행 프로세스

단계

전역 최적화 알고리즘 프로세스

1 단계

초기 노드 SO를 OPEN 테이블 f(S0)에 넣습니다.

2 단계

OPEN 테이블이 비어 있으면 문제에 대한 해결책이 없으며 종료됩니다.

3단계

OPEN 테이블의 첫 번째 노드(노드 n으로 기록됨)를 가져와서 CLOSED 테이블에 넣습니다.

4단계

노드 n이 대상 노드인지 확인합니다. 그렇다면 문제에 대한 해결책을 찾아 종료하세요.

5단계

노드 n을 확장할 수 없는 경우 2단계로 이동합니다.

6단계

노드 n을 확장하고, 휴리스틱 함수 f(x)를 사용하여 각 자식 노드의 추정값을 계산하고, 각 자식 노드에 대해 부모 노드에 대한 포인터를 구성하고, 이 자식 노드를 OPEN 테이블로 보낸 다음, 노드의 모든 노드를 계산합니다. OPEN 테이블 추정값을 작은 것부터 큰 것 순으로 정렬한 후 2단계로 이동합니다.

(2) A* 검색 알고리즘

        A* 검색 알고리즘은 전역 최적화 알고리즘에 비해 변경 사항이 매우 적고 몇 가지 기능만 추가했을 뿐입니다. 전역 최적화, A 알고리즘 및 A* 알고리즘의 정의와 차이점은 표 6에 나와 있습니다.

표 6 A 알고리즘과 A* 알고리즘의 정의

정의

전역 최적화 알고리즘 프로세스

정의 1

GRAPHSEARCH 과정에서 f(x)=g(x)+h(x)를 기준으로 OPEN 테이블을 재배열하면 이 과정을 알고리즘 A라고 한다.

정의 2

알고리즘 A에서 h(x)<=h*(x)가 모든 x에 대해 성립하면 h(x)는 일종의 보수적 추정을 나타내는 h*(x)의 하한이라고 합니다.

정의 3

h(x)의 하한 h*(x)를 휴리스틱 함수로 사용하는 A 알고리즘을 A* 알고리즘이라고 합니다.

        A*算法是在A算法的基础上,每生成一个新节点,即查找closed表,如果closed表中有相同排列的结点,那么则比较他们的权重(f(n)),如果新节点的权重更小,则替代原结点,即,刷新原结点的深度,这样就很有可能找到更短、更快的到达目标结点的路径。A*算法执行的流程图如图 6所示

 

beda9d87ab2f4dbd8ac96412da3e5a54.png

图 6 A*算法执行流程图

(三)宽度优先算法

        宽度优先算法是一种盲目搜索算法,即,蛮力法;需要一个Open表,把起始节点放入Open表中,如果Open表为空,则无解并退出,否则,继续把第一个节点n从Open表中移除,并放入CLOSED扩展节点表中。这里要考察节点n是不是目标节点,如果是的话,就代表求出答案了,则可以退出,如果节点n往下不可以接着扩展了,那么转到第二步,继续从OPEN表中选节点,放入CLOSED表考察。如果节点n可扩展,则把所有n的子节点放入OPEN表的尾部,配置父节点指针,继续转Step2开始下一轮判断。算法流程如表 7所示。

表 7 宽度优先算法流程表

步骤

宽度优先算法流程

Step1

把起始节点放到OPEN表中

Step2

如果OPEN是个空表,则没有解失败退出;否则继续

Step3

把第一个节点 (节点n) 从OPEN表移出,并把它放入CLOSED扩展节点表中

Step4

考察节点n是否为目标节点。如果是,则求得了问题的解,退出

Step5

如果节点n不可扩展,则转Step2

Step6

把n的所有子节点放到OPEN表的尾部,并为其配置指向父节点的指针然后转第Step2步

 

算法流程图如图 7所示

4e81c4d7f5394ef0b4e50f0fe3ca7898.png

 

图 7 宽度优先算法流程图

 

(四)四种估值函数

        对于全局择优和A*搜索这两种算法,为了求取最优解,可以设置不同的估值函数,测试看不同的效果。这里选择了四种估值函数,估值函数的解释如图 8所示。

5933bd8140a740c88e0f3a5dc14fa1c0.png

图 8 四种估值函数

 

        这里的Evaluate1和Evaluate2的启发函数定义为当前结点与目标结点差异的度量,这里的“差异”,可以是当前节点与目标节点相比,位置不符的数字个数,也可以是当前节点和目标节点格局相比,位置不符的数字移动到目标节点中对应位置的最短距离之和。

        Evaluate3定义为每一对逆序数字乘以一个倍数,即该状态下 逆序数个数与目标状态逆序数个数的绝对值乘以权重。

        Evaluate4是为了克服仅在计算数字逆序数字量策略的局限,启发函数定义为位置不符数字个数的总和+3倍逆序数字序列。

 

三、状态记录

前端使用了el-table表格,对八数码路径查找执行时的用时、步数等进行了数据的持久化存储,并利用axios调用后端接口,实现了数据的回显。在前端显示的表格中,部分数据如图 9所示。

72b52f808a754938ac9a2736d36037db.png

 

图 9 数据记录表的部分数据

【编码实现】

        由于主要功能的实现是通过估值函数类&&算法类实现的,所以在这里,仅对四种估值函数和三种算法做介绍。

一、估值函数

(一)估值函数1

        启发函数 h(n)定义为当前节点与目标节点差异的度量:即当前节点与目标节点格局相比,位置不符的数字个数。其中,估值函数Evaluate1的代码如下:

    public int Evaluate1(int a[][],int[][] num_Finish) {

        int n = 0;

        for (int i = 0; i < 3; ++i) {

            for (int j = 0; j < 3; ++j) {

                if (a[i][j] != num_Finish[i][j])

                    ++n;//找两个状态有多少个格子位置错了

            }

        }

        return n;

    }

(二)估值函数2

        启发函数 h(n)定义为当前节点与目标节点距离的度量:当前节点与目标节点格局相比,位置不符的数字移动到目标节点中对应位置的最短距离之和。其中估值函数Evaluate2如下:

public int Evaluate2(int a[][],int[][] num_Finish) {

        int h = 0;// 代价

        //找到这个数在初始状态和目标状态的位置在哪

        for (int i = 0; i < 9; ++i) {

            int m, n;

            for (m = 0; m < 9; ++m)

//二维数组遍历找0-8中的数字i,找到就往下

                if (a[m / 3][m % 3] == i) break;

            for (n = 0; n < 9; ++n)

                if (num_Finish[n / 3][n % 3] == i)//找到就往下

                    break;

            //行序差+列序差

            h += Math.abs((m / 3) - (n / 3)) + Math.abs((m % 3) - (n % 3));

        }

        return h;

    }

 

(三)估值函数3

        启发函数 h(n)定义为每一对逆序数字乘以一个倍数,即h(n)=|该状态逆序数个数-目标状态逆序数个数| * 权重

public int Evaluate3(int a[][],int[][] num_Finish) {

int weight=Math.abs(ReverseNumber(a)-ReverseNumber(num_Finish));

return weight*Math.abs(ReverseNumber(a)-ReverseNumber(num_Finish));

}

 

(四)估值函数4

        为克服了仅计算数字逆序数字数目策略的局限 启发函数 h(n)定义为位置不符数字个数的总和+ 3 倍数字逆序数目。

public int Evaluate4(int a[][],int[][] num_Finish) {

return Evaluate1(a,num_Finish) + 3 * Math.abs(ReverseNumber(a) - ReverseNumber(num_Finish));

}

 

二、搜索算法

(一)全局择先算法

        全局择优算法的执行流程是先把初始节点S0放入OPEN表中,根据当前结点的深度和当前结点和目标结点差异的度量,计算出启发函数f(S0),如果OPEN表为空,那么问题没有解,直接退出程序,如果OPEN表还不为空,那就把OPNE表的第一个节点n放入CLOSED表,考察该节点是否是目标结点,如果是的,那就可以直接得出问题解,从而退出程序,如果还没找到目标节点,则节点n不可以扩展,转到Step2,如果节点n可以扩展,则扩展该节点,用估值函数f(n)计算每个节点的估价值,并给每个子结点配置指向父结点的指针,把这些子节点都放入OPEN表中,然后对OPEN表中的所有节点按照估值函数f(n)的大小进行排序,再转到Step2去,全局择优算法的执行流程如表 5所示。其中,全局择先函数如下:

public int[][][]  GlobalSearch2(long info[],int nEvaluate,int[][] num_Begin,int[][] num_Finish) {



        starTime = System.nanoTime();



        Vector<Node> OPEN=new Vector<Node>();

        Vector<Node> CLOSED=new Vector<Node>();



        // 添加根节点

        OPEN.add(new Node(num_Begin, 0, evaluateService.ReverseNumber(num_Begin), 0, null, evaluateService.Evaluate(num_Begin, 0, nEvaluate,num_Finish)));



        while(!OPEN.isEmpty()) {



            // 计算可达性,限制搜索深度

            if (!isAvailable(OPEN.get(0).num,num_Finish) || OPEN.get(0).depth > MAX_SEARCH_DEPTH) {

                return null;

            }



            CLOSED.add(OPEN.get(0));

            OPEN.remove(0);



            if(isEqual(CLOSED.lastElement().num,num_Finish)) {



                return Route2(CLOSED.lastElement(),info);

            }

            else {



                Node father=CLOSED.lastElement();

                int i = 0, j = 0;



                // 找到空格

                for (int k = 0; k < 9; ++k) {

                    if (father.num[k / 3][k % 3] == 0) {

                        i = k / 3;

                        j = k % 3;

                        break;

                    }

                }



                int dir=father.direction,dep=father.depth;

                int a[][]=new int[3][3];



                //1 上

                if (i != 0&& dir!=2) {



                    for (int m = 0; m < 3; ++m) {

                        for (int n = 0; n < 3; ++n) {

                            a[m][n]=father.num[m][n];

                        }

                    }



                    a[i][j]=a[i-1][j];

                    a[i-1][j]=0;



                    OPEN.add(new Node(a, dep + 1, evaluateService.ReverseNumber(a), 1, father,evaluateService.Evaluate(a, dep + 1, nEvaluate,num_Finish)));



                }

              //参照“1 上”,执行下、左、右三个方向的扩展

        }

        return null;



    }

 

(二)A*搜索算法

        A*搜索算法相对于全局择优算法改动非常小,只增加了个别函数,因此在此处省略。

(三)宽度优先算法

        宽度优先算法是一种盲目搜索算法,即,蛮力法;需要一个Open表,把起始节点放入Open表中,如果Open表为空,则无解并退出,否则,继续把第一个节点n从Open表中移除,并放入CLOSED扩展节点表中。这里要考察节点n是不是目标节点,如果是的话,就代表求出答案了,则可以退出,如果节点n往下不可以接着扩展了,那么转到第二步,继续从OPEN表中选节点,放入CLOSED表考察。如果节点n可扩展,则把所有n的子节点放入OPEN表的尾部,配置父节点指针,继续转Step2开始下一轮判断。算法流程如表 7所示。WideSearch2函数如下所示:

public int[][][] WideSearch2(long info[],int[][] num_Begin,int[][] num_Finish) {

    int[][][] result = {};

    starTime = System.nanoTime();

    Vector<Node> OPEN = new Vector<Node>();//声明不定长数组
    Vector<Node> CLOSED = new Vector<Node>();

    // 把初始节点送入OPEN表
    OPEN.add(new Node(num_Begin, 0, evaluateService.ReverseNumber(num_Begin), 0, null, 0));

    //初始节点是否是目标节点?
    if(isEqual(OPEN.get(0).num,num_Finish)){

        String s = "-----第1步-----\n";

        for (int k = 0; k < 3; ++k) {
            for (int j = 0; j < 3; ++j) {
                s += CLOSED.lastElement().num[k][j] + "  ";
                //最后一个元素的节点数据
                result[0][k][j] = CLOSED.lastElement().num[k][j];
            }
            s += "\n";
        }
        s += "-----结束-----\\n";
        info[0] = 0; // 移动步数
        info[1] = System.nanoTime() - starTime;

        return result;
    }

    while (!OPEN.isEmpty()) {

        // 计算可达性,限制搜索深度
        if (!isAvailable(OPEN.get(0).num,num_Finish) || OPEN.get(0).depth > MAX_SEARCH_DEPTH) {
            return null;
        }

        CLOSED.add(OPEN.get(0));
        OPEN.remove(0);

        int i = 0, j = 0;

        // 找到空格
        for (int k = 0; k < 9; ++k) {
            if (CLOSED.lastElement().num[k / 3][k % 3] == 0) {
                i = k / 3;
                j = k % 3;
                break;
            }
        }

        int dir = CLOSED.lastElement().direction, dep = CLOSED.lastElement().depth;
        int a[][] = new int[3][3];

        // 1 上
        if (i != 0 && dir != 2) {

            for (int m = 0; m < 3; ++m) {
                for (int n = 0; n < 3; ++n) {
                    a[m][n] = CLOSED.lastElement().num[m][n];
                }
            }

            a[i][j] = a[i - 1][j];
            a[i - 1][j] = 0;

            Node temp = new Node(a, dep + 1, evaluateService.ReverseNumber(a), 1, CLOSED.lastElement(), 0);

            // 子节点是否是目标节点?
            if (isEqual(temp.num,num_Finish)) {
                return Route2(temp, info);
            }
            OPEN.add(temp);

        }
        //参照“1 上”,执行下、左、右三个方向的扩展

 
    }

    return null;

}

【实验结果与分析】

一、执行逻辑

介绍

运行截图

 

初始化界面由九宫格的初始状态和目标状态、估值函数选择区、算法选择区、路径打印区、结果记录表组成。

4bc3a3874f7240128668db5961efe92a.png

 

 

 

打乱九宫格后,点击“全局择先”按钮,跳出confirm框,询问是否确定进行路径查找。

8e57103f20b3460fbbcc026f3bdf328f.png

 

 

 

选择确定后,打印出初始状态到目标状态需要走的步数,显示在标签页内嵌的表格中。

b4e367e2db4b405da4d02cbcae619713.png

 

 

 

 

刷新界面后数据会从数据库回显到表格中,三种算法、四种估值函数均可用

10087c17e34a4a62977c4405e90b86fc.png

 

 

 

二、结果测试

信息

测试截图

--测试用例1

 

 

 

全局择先

h1

10步

132ms

4a417789f1a24e1992a5cccf92007d24.png

 

 

--测试用例2

 

 

 

 

全局择先

h2

10步

166ms

e32a43211bc6473388edb83aed425007.png

 

 

--测试用例3

 

 

 

 

A*搜索

h3

10步

319ms

fd09151e861646ba882b696684d934da.png

 

 

--测试用例4

 

 

 

 

宽度优先

10步

655ms

909fb69e5a55423494c935501c2c3df3.png

 

 

 

三、结果分析

从上面的对比结果来看可以得到以下的结果分析:

  1. 选取不同的估值函数,有时还是会得到相同的路径结果;
  2. A*搜素和全局择先的运行速度比较接近;
  3. 在初始状态和目标状态相同的情况下,宽度优先的效率低于两种启发式搜索算法,即全局择先和A*算法。

 

【总结】

        现在是凌晨3点20,课设报告终于收尾了,回想这几天的课设制作,也确实是感想颇深。

    我是上周四开始写课设的,第一天,当然是储备基础知识,像典型的A*算法,还有全局择先,宽度优先这些,都认真学了一遍。当然还构思了一下课设的思路,因为不限语言,不限技术,脑子里一大堆天马行空的想实现的东西,一开始设计是想对比三种算法的优劣+用深度学习的方法训练一下它去玩这个华容道,看看到底是跑出个什么东西,或者就是想做一个联网的平台,可以让用户自行在网站pk拼图速度,或是打乱让用户还原,比较看和三种算法相比,谁的还原步数少。还想了一个点子就是用户自行上传图片––>裁剪成九宫格的样子––>玩自定义拼图,但都碍于时间有限,就没做了。

    当然,由于我目前为止主要学过的也是全栈开发,所以,在这次的课设中,最终呈现的效果,就是一个前后端交互的网页端系统了。

    周五,写课设的第二天,想着把后端架子先搭好,结果一下是pom出问题,找半天发现是maven仓库路径写错了,一下又是springboot报错,找半天,然后对着重新敲一遍就又可以运行了,气得我肝疼。好不容易加载出Spring标标了,结果又因为tomcat配的是2.9.5以上版本,配高了它有些东西不认识,又得重新写配置,总而言之就是突然发现,从头搭一个Springboot项目,对经验不丰富的人确实挺困难的呐。

     然后下午的时候,开始写逻辑,数据库如果做了实时回显的话,倒也不会显得我那张记录存储表那么没用,还是遇到了很玄学的问题,局部刷新不了+数据回显它只读第一次的,后面无法修改数据+reload整个界面又把我的路径给刷没了,所以最后这个问题就没解决了,后期有时间的话还是做成分页,elementui里倒是有个el-pagenation,今天试了绑定不了数据就没做了,下次有时间再试试。

    总之,后端遇到的困难主要是搭框架,还有就是写算法逻辑的问题,估值函数还挺好写的,三个算法对着流程图敲就完了,但一开始设计的时候,图方便,设计的字符串输出,结果就是,前端写完之后,改的我肝疼。

    分别测试了三种接口写法,一种是String直接打印结果的,非常丑,而且,不好裁。第二种是返回的三维数组,相当于是吧每种状态都存在了数组中,而每种状态本身就是个二维数组,但因为用了是数组,数组就肯定定长,定长没排满,结果就是一堆0,太心累了,找了一堆办法没裁好,最后想着,要不要就还是用Node存储Service层返回的结果,结果因为设计的问题,它递归了,嵌套了十几二十层,才能找到最里面的节点,前端拿到的json文件,一看,我是真束手无策。最后求助大佬,大佬用三维数组的那个接口,给我裁好了,但我还是追求界面美观,于是又花了一个多小时,把路径结果从直线存成了3x3的了。

    그러다가 셋째날인 토요일에 프론트엔드를 쓰기 시작했습니다. 프론트엔드는 전혀 어렵지 않습니다. 구공게는 4x4 화용도입니다. 웹사이트를 내려서 4x4 로직을 이해하고 3x3 으로 바꿨습니다. 이걸 바꾸는게 정말 피곤합니다. 4x4 로직은 n%4 이고, nπ4, 그러면 3x3은 4를 3으로 바꾼다는 뜻인가요? 아니요, 4x4 9각형 그리드는 4방향으로 움직일 수 있는데, 직접 바꾸면 분명 문제가 있을 것입니다.대략적으로 이해할 수 있어야 한다는 논리입니다. 저는 주로 롤백 기능에 매달렸는데 그 사람이 쓴 건 n*3-v 같은 거에요. 제 생각에는 3? 이것은 옳지 않습니다.결과적으로 이것은 이동방향과 걸음수를 기록하는데 사용됩니다.3x3사각형에서 이동하는 방법은 8가지가 있으므로 0~8이 되고 두칸을 움직이고 한칸을 움직이면 거기에 있습니다. plc에 대한 수정 사항입니다. 이중 관계이므로 스위치에서 중단까지 주문에 대한 요구 사항이 여전히 있습니다. 그러나 마침내 그것을 쓴 사람은 나였습니다.

    요컨대, 코스 설계의 최종 결과는 꽤 좋은 편이지만, 유일한 단점은 데이터베이스가 여전히 최종적으로 실시간 에코를 달성하지 못했다는 점입니다. 데이터베이스에는 설계 중 초기 상태 및 종료 상태에 대한 기록이 부족했으며, 커스텀 퍼즐 기능은 안 써져 있고, 캔버스를 자동으로 복원하거나 9칸 그리드를 깨뜨리는 기능도 있는데 실제로 꼭 필요한 기능인데 안 써져 있네요. 또한 엔터티 클래스를 설계할 때 변수를 보호하는 것을 잊어버렸기 때문에 모든 변수를 공개하는 것은 매우 안전하지 않습니다.

   하지만 내가 작성한 내용은 여전히 ​​괜찮다고 생각합니다. 이것이 다시 나 자신에게 도전해야 합니다. 3일 만에 Springboot 프로젝트를 완료하면 풀 스택 엔지니어가 되는 데 한 걸음 더 가까워집니다. 또한, 이번 강좌설계를 통해 탐욕탐색, 휴리스틱 탐색 등의 알고리즘도 배웠는데, 이것이 앞으로의 학습에 도움이 될 것이라고 믿습니다.

    드디어 강좌 설계가 완성되었습니다~ 이번 학기에도 열심히 가르쳐주신 선생님께 감사드립니다!

 

Guess you like

Origin blog.csdn.net/shengshanlaolin_/article/details/131483963