前言:本篇博客是建立在这篇博文的基础上,是个人经过实际操作之后对其算法的改进

一、八数码问题

一个九宫格,有八个数字1-8已经确定位置,剩下一个空格以0表示,0可以和上下左右的数字交换位置。

如果给定一个初始状态1,一个目标状态2,求解从状态1到状态2最少要移动多少步

二、A*算法

1、算法定义及公式

算法是一种静态路网中求解最短路径最有效的直接搜索方法,公式表示为: f(n)=g(n)+h(n),其中:

f(n) 是从初始状态经由状态n到目标状态的代价估计,称作估计函数

d(n) 是在状态空间从初始状态到状态n的实际代价,称作节点深度

h(n) 是从状态n到目标状态的最佳路径的估计代价,称作启发函数

(对于路径搜索问题,状态就是图中的节点,代价就是距离)

2、A*算法设计

三、八数码问题算法设计步骤

1、设计八数码节点状态结构

存储结构采取一维数组int[] num,估计函数f(n)、节点深度d(n)、启发函数h(n),以及要定义每一个状态的父状态。

这里额外设置了一个answer的线性表,用于存储最终答案路径(为什么额外设计它后面会解释)

后面的getter和setter不额外截图了

2、可达性判断函数设计(计算两个节点之间的可达性)

这里是通过计算八数码节点的逆序数判断,两个节点状态的逆序数必须同奇或者同偶才是可达状态(也就是初始状态的逆序数+目标状态的逆序数=偶数)。什么是逆序数呢?

如果一对数的前后位置与大小顺序相反,也就是如果较大的数在较小的数之前,那么就算一个逆序对,逆序数+1。逆序数是偶数的排列称作偶排列,是奇数的排列称作奇排列。如在 2,4,3,1 中,21,43,41,31是逆序,逆序数是4,是偶排列。计算八数码节点的逆序数时必须要把代表空格的0去掉,这里是原博客没有注意的地方,判断可达性的函数修改如下:

3、估计函数的设计

估计函数是由两部分构成的,节点深度d(n)其实就是当前已经走的步数,不用额外设计函数;启发函数h(n)是比较重要的一个部分,启发函数的设计直接影响了估计函数的效率,有几种定义方法:

当前节点与目标节点差异的度量 => 当前结点与目标节点相比,位置不符的数字个数

当前节点与目标节点距离的度量 => 当前结点与目标节点格局位置不符的数字移动到目标节点中对应位置的最短距离之和

每一对逆序数字的某倍数

位置不符的数字个数的总和+逆序数的三倍

这里选择的是第一种,计算当前节点与目标节点相比,有多少个数字的位置不符

4、A*算法设计

依照上面算法流程图设计就可以了。额外需要注意的是:

open表和close表使用ArrayList保存状态,open表存放所有的状态,close表则存放在搜索过程中一些较优的状态,但是close表并不是最终我们走的路径。因为我们要在搜索完成找到目标状态之后,根据父状态还原出原路径,因此原博客中的算法只能逆序输出路径。所以我额外设计了一个线性表answer就是为了在还原过程中保存我们所走的路径,从而实现正序的输出。对输出路径的函数修改:

5、其他逻辑函数设计

对子状态的f(n)的排序

根据八数码确定0在一维数组中下标的函数

判断当前状态是否在open表中的去重函数

判断移动的函数(判断是否可以上、下、左、右移动)

实现移动的函数(实现0在八数码中的上、下、左、右移动)

根据一维数组输出八数码(3*3)格式的函数

6、源码

import java.util.ArrayList;

import java.util.Arrays;

import java.util.Collections;

import java.util.Scanner;

@SuppressWarnings("rawtypes")

public class EightPuzzle implements Comparable{

privateint[] num = new int[9];

privateint evaluation; //估计函数f(n):从起始状态到目标的最小估计值

private int depth; //d(n):当前的深度,即走到当前状态的步骤

private int misposition; //启发函数 h(n):到目标的最小估计(记录和目标状态有多少个数不同)

private EightPuzzle parent; //当前状态的父状态

private ArrayList answer = new ArrayList(); //保存最终路径

public int[] getNum() {returnnum;

}

publicvoid setNum(int[] num) {this.num =num;

}

publicintgetDepth() {returndepth;

}

publicvoid setDepth(intdepth) {this.depth =depth;

}

publicintgetEvaluation() {returnevaluation;

}

publicvoid setEvaluation(intevaluation) {this.evaluation =evaluation;

}

publicintgetMisposition() {returnmisposition;

}

publicvoid setMisposition(intmisposition) {this.misposition =misposition;

}

public EightPuzzle getParent() {returnparent;

}

publicvoidsetParent(EightPuzzle parent) {this.parent =parent;

}/**

* 判断当前状态是否为目标状态

* @param target

* @return*/publicbooleanisTarget(EightPuzzle target){returnArrays.equals(getNum(), target.getNum());

}/**

* 求估计函数f(n) = g(n)+h(n);

* 初始化状态信息

* @param target*/publicvoidinit(EightPuzzle target){int temp = 0;for(int i=0;i<9;i++){if(num[i]!=target.getNum()[i])

temp++; //记录当前节点与目标节点差异的度量

}this.setMisposition(temp);if(this.getParent()==null){this.setDepth(0); //初始化步数(深度)

}else{this.depth = this.parent.getDepth()+1;//记录步数

}this.setEvaluation(this.getDepth()+this.getMisposition());//返回当前状态的估计值

}/**

* 求逆序值并判断是否有解,逆序值同奇或者同偶才有解

* @param target

* @return 有解:true 无解:false*/publicbooleanisSolvable(EightPuzzle target){int reverse = 0;for(int i=0;i<9;i++){for(int j=0;j

if(num[j]>num[i] && num[j]!=0 && num[i]!= 0)

reverse++;if(target.getNum()[j]>target.getNum()[i] && target.getNum()[j]!=0 && target.getNum()[i]!=0)

reverse++;

}

}if(reverse % 2 == 0)return true;return false;

}/**

* 对每个子状态的f(n)进行由小到大排序

**/@Override

publicintcompareTo(Object o) {

EightPuzzle c=(EightPuzzle) o;return this.evaluation-c.getEvaluation();//默认排序为f(n)由小到大排序

}/**

* @return 返回0在八数码中的位置*/publicintgetZeroPosition(){int position = -1;for(int i=0;i<9;i++){if(this.num[i] == 0){

position=i;

}

}returnposition;

}/**

* 去重,当前状态不重复返回-1

* @param open 状态集合

* @return 判断当前状态是否存在于open表中*/publicint isContains(ArrayListopen){for(int i=0; i

}

}return -1;

}/**

* 一维数组

* @return 小于3(第一行)的不能上移返回false*/publicbooleanisMoveUp() {int position =getZeroPosition();if(position<=2){return false;

}return true;

}/**

*

* @return 大于6(第三行)返回false*/publicbooleanisMoveDown() {int position =getZeroPosition();if(position>=6){return false;

}return true;

}/**

*

* @return 0,3,6(第一列)返回false*/publicbooleanisMoveLeft() {int position =getZeroPosition();if(position%3 == 0){return false;

}return true;

}/**

*

* @return 2,5,8(第三列)不能右移返回false*/publicbooleanisMoveRight() {int position =getZeroPosition();if((position)%3 == 2){return false;

}return true;

}/**

*

* @param move 0:上,1:下,2:左,3:右

* @return 返回移动后的状态*/public EightPuzzle moveUp(intmove){

EightPuzzle temp= newEightPuzzle();int[] tempnum = (int[])num.clone();

temp.setNum(tempnum);int position = getZeroPosition(); //0的位置

int p=0; //与0换位置的位置

switch(move){case 0:

p= position-3;

temp.getNum()[position]=num[p];break;case 1:

p= position+3;

temp.getNum()[position]=num[p];break;case 2:

p= position-1;

temp.getNum()[position]=num[p];break;case 3:

p= position+1;

temp.getNum()[position]=num[p];break;

}

temp.getNum()[p]= 0;returntemp;

}/**

* 按照3*3格式输出*/publicvoidprint(){for(int i=0;i<9;i++){if(i%3 == 2){

System.out.println(this.num[i]);

}else{

System.out.print(this.num[i]+" ");

}

}

}/**

* 将最终答案路径保存下来并输出*/publicvoidprintRoute(){

EightPuzzle temp= null;int count = 0;

temp= this;

System.out.println("----------开始移动----------");while(temp!=null){

answer.add(temp);

temp=temp.getParent();

count++;

}for(int i=answer.size()-1 ; i>=0 ; i--){

answer.get(i).print();

System.out.println("--------------------");

}

System.out.println("最小移动步数:"+(count-1));

}/**

*

* @param open open表

* @param close close表

* @param parent 父状态

* @param target 目标状态*/publicvoid operation(ArrayList open,ArrayListclose,EightPuzzle parent,EightPuzzle target){if(this.isContains(close) == -1){//如果不在close表中

int position = this.isContains(open);//获取在open表中的位置

if(position == -1){//如果也不在open表中

this.parent = parent;//指明它的父状态

this.init(target);//计算它的估计值

open.add(this);//把它添加进open表

}else{//如果它在open表中

if(this.getDepth() < open.get(position).getDepth()){//跟已存在的状态作比较,如果它的步数较少则是较优解

open.remove(position);//把已经存在的相同状态替换掉

this.parent =parent;this.init(target);

open.add(this);

}

}

}

}

@SuppressWarnings("unchecked")

public staticvoidmain(String args[]){//定义open表

ArrayList open = new ArrayList();

ArrayList close = new ArrayList();

EightPuzzle start= newEightPuzzle();

EightPuzzle target= newEightPuzzle();//int stnum[] = {8,6,7,2,5,4,3,0,1};//int tanum[] = {6,4,7,8,5,0,3,2,1};

Scanner s= new Scanner(System.in);int stnum[] = new int[9];int tanum[] = new int[9];

System.out.println("请输入初始状态:");for(int i = 0; i< 9; i++){

stnum[i]=s.nextInt();

}

System.out.println("请输入目标状态:");for(int j= 0; j< 9; j++){

tanum[j]=s.nextInt();

}

s.close();

start.setNum(stnum);

target.setNum(tanum);long startTime=System.currentTimeMillis();if(start.isSolvable(target)){//初始化初始状态

start.init(target);

open.add(start);while(open.isEmpty() == false){

Collections.sort(open);//按照evaluation的值排序

EightPuzzle best = open.get(0); //从open表中取出最小估值的状态并移出open表

open.remove(0);

close.add(best);if(best.isTarget(target)){//输出

best.printRoute();long end=System.currentTimeMillis();

System.out.println("程序运行 "+ (end-startTime) +" ms");

System.exit(0);

}intmove;//由best状态进行扩展并加入到open表中

//0的位置上移之后状态不在close和open中设定best为其父状态,并初始化f(n)估值函数

if(best.isMoveUp()){//可以上移的话

move = 0;//上移标记

EightPuzzle up = best.moveUp(move);//best的一个子状态

up.operation(open, close, best, target);

}//0的位置下移之后状态不在close和open中设定best为其父状态,并初始化f(n)估值函数

if(best.isMoveDown()){

move= 1;

EightPuzzle down=best.moveUp(move);

down.operation(open, close, best, target);

}//0的位置左移之后状态不在close和open中设定best为其父状态,并初始化f(n)估值函数

if(best.isMoveLeft()){

move= 2;

EightPuzzle left=best.moveUp(move);

left.operation(open, close, best, target);

}//0的位置右移之后状态不在close和open中设定best为其父状态,并初始化f(n)估值函数

if(best.isMoveRight()){

move= 3;

EightPuzzle right=best.moveUp(move);

right.operation(open, close, best, target);

}

}

}elseSystem.out.println("目标状态不可达");

}

}

View Code

八数码java_八数码问题-A*算法-Java实现相关推荐

  1. 八皇后java_八皇后(JAVA算法实现)

    在学习现代软件工程构建之法这门课时,老师要求发表一篇博客,使用JAVA算法实现八皇后问题的求解.写这篇博客时,我学习了一些其他的博客,自己无法解决时,向他人学习也是一种方法. 国际西洋棋棋手马克斯·贝 ...

  2. 发牌游戏 java_解析扑克牌游戏发牌算法——java实现

    我们都玩过扑克牌的小游戏,有没有想过扑克牌的发牌是怎样实现的呢? 首先,我们手里有一副牌,假设去掉大小鬼,我们手里的牌用数字1-52表示.我们每次只取出一个数字,那我们如何取出这些数字并且使每次取出的 ...

  3. 蚁群算法画图java_[转载]简单蚁群算法 + JAVA实现蚁群算法

    一 引言 蚁群算法(ant colony optimization,ACO),又称蚂蚁算法,是一种用来在图中寻找优化路径的机率型技术.它由Marco Dorigo于1992年在他的博士论文中引入,其灵 ...

  4. 猎豹怎么运行java_猎豹网校 数据结构与算法 Java语言 JAVA语言视频教程(火评)...

    01.NetBeans_下载和安装.mp4  02.JavaDS_数据结构和算法的概述.mp4 03.JavaDS_数组基础知识.mp4 04.JavaDS_有序数组和二分查找.mp4 05.Java ...

  5. c语言八数码A星算法代码解析,八数码问题c语言a星算法详细实验报告含代码解析...

    八数码问题c语言a星算法详细实验报告含代码解析 (13页) 本资源提供全文预览,点击全文预览即可全文预览,如果喜欢文档就下载吧,查找使用更方便哦! 14.9 积分 一.实验内容和要求 八数码问题:在3 ...

  6. 机器学习之基于A*搜索解决八数码问题15数码问题

    针对hdu1043,来说一下A* 搜索.这道题不一定用A* 算法,还可以用双向bfs.但是A*搜索更快,在人工智能方面应用也很广泛. A* 搜索不是像深度优先搜索算法和广度优先搜索算法一样的傻瓜式的埋 ...

  7. 八十七、Python | 十大排序算法系列(上篇)

    @Author:Runsen @Date:2020/7/10 人生最重要的不是所站的位置,而是内心所朝的方向.只要我在每篇博文中写得自己体会,修炼身心:在每天的不断重复学习中,耐住寂寞,练就真功,不畏 ...

  8. 八十四、Python | Leetcode回溯算法系列

    @Author:Runsen @Date:2020/7/7 人生最重要的不是所站的位置,而是内心所朝的方向.只要我在每篇博文中写得自己体会,修炼身心:在每天的不断重复学习中,耐住寂寞,练就真功,不畏艰 ...

  9. 八十二、Python | Leetcode贪心算法系列

    @Author:Runsen @Date:2020/7/5 人生最重要的不是所站的位置,而是内心所朝的方向.只要我在每篇博文中写得自己体会,修炼身心:在每天的不断重复学习中,耐住寂寞,练就真功,不畏艰 ...

  10. C++八皇后拼图,打印所有的算法(附完整源码)

    八皇后拼图,打印所有的算法 八皇后拼图,打印所有的算法的完整源码(定义,实现,main函数测试) 八皇后拼图,打印所有的算法的完整源码(定义,实现,main函数测试) #include <ios ...

最新文章

  1. linux中的gun含义,linux中gun的含义
  2. Java 8 Stream Api 中的 map和 flatMap 操作
  3. python使用open打开文件时显示文件不存在-Python打开文件open()的注意事项
  4. 20164317《网络对抗技术》Exp9 Web安全基础
  5. pip install lxml 总是失败
  6. error while loading shared libraries: xxx.so.0:cannot open shared object file: No such file or
  7. vue根据url获取内容axios_vue+vuex+axios从后台获取数据存入vuex,组件之间共享数据...
  8. 结构化数据建模——titanic数据集的模型建立和训练(Pytorch版)
  9. 在Ubuntu上安装Keras深度学习框架
  10. python3安装requests_小小-Centos7安装Python3并安装Requests的方法
  11. 【java】浅析JDK中ServiceLoader的源码
  12. 被问到一个简单的脚本后
  13. Step by Step-构建自己的ORM系列-索引
  14. windows + hadoop + eclipse 过程记录
  15. 用python写邮件和附件自动生成发送系统
  16. 计算机概论综述,计算机组成原理唐朔飞PPT 第1章 计算机系统概论综述.ppt
  17. ZoneAlarm 不错的防火墙软件
  18. java 保龄球游戏开发_2019-11-08-基于TDD实现的java版本的保龄球规则(实体类)
  19. 达内java月考试题_达内java1512第二次月考(附答案)doc.doc
  20. 河北源达靠谱吗?股市如战场,可靠的选股软件很关键

热门文章

  1. codesys的设备树重点详解
  2. Android如何修改手机文件名称
  3. 软件测试面试题:简述什么是静态测试、动态测试、黑盒测试、白盒测试、α测试 β测试?
  4. linux文件管理系统答辩ppt,Linux操作系统ppt--第9次文件管理分析.ppt
  5. 知识图谱之社交网络分析(SNA)之python处理
  6. 服务器fs改变文件内容,SeaweedFS文件系统
  7. java 抓取搜狗微信_大虾们,求帮助……用httpclient 进行获取微信搜狗公众号文章问题...
  8. 解锁Insyde的BIOS隐藏设置
  9. Dijkstra最短路算法
  10. Pr 音频效果参考:混响