0X00  定义

  首先要明确一下什么是A*算法和八数码问题?

  A*(A-Star)算法是一种静态路网中求解最短路径最有效的直接搜索方法也是一种启发性的算法,也是解决许多搜索问题的有效算法。算法中的距离估算值与实际值越接近,最终搜索速度越快。启发中的估价是用估价函数表示的,如:

f(n) = g(n) + h(n) 

其中f(n) 是节点n的估价函数,g(n)实在状态空间中从初始节点到n节点的实际代价,h(n)是从n到目 标节点最佳路径的估计代价。其中最重要的是h(n)函数,要求

h(n)<h'(n) 

其中h'(n)为实际中当前状态要到达目标状态的步骤数。

  八数码问题就是在一个3*3的矩阵中摆放1-8一共八个数字,还有一个空余的位置用于移动来改变当前的位置来达到最终的状态。如下图

0X01  分析八数码问题

  首先我们要简化一下八数码问题,我们移动数字就是相当于移动空格。这样我们就将问题简化为空格的移动,空格移动的状态只有4种:上、下、左、右。然而在八数码问题中并不是每次空格的移动都有四种状态,我们要判断在当前位置也移动的状态才能移动,我们还要去掉一种状态就是当前状态的父状态,因为如果我们移动到父状态则相当于回退了一步。

  然后,我们要关心的就是给定的初始化状态是否能够通过移动而达到目标状态。这就涉及到了数学问题,就是如果初始状态和目标状态的逆序值同为奇数或同为偶数则可以通过有限次数的移动到达目标状态,否则无解。

  既然我们已经清楚了空格移动的方式,我们讨论一下空格的几种移动的可能方式:

对应的状态如图所示。

0X02 算法的实现

  A*算法的实现有一个具体的流程图:

 

我们使用A*来解决八数码问题,首先我们定义一下f(n),g(n)和h(n)。

  f(n):估计从初始状态到目标状态的代价。

  g(n):从初始状态到当前状态的实际代价。

  h(n):当前状态与目标状态的错位数。

首先我们定义八数码一个状态中的属性:

    private int[] num = new int[9];
private int depth; //当前的深度即走到当前状态的步骤
private int evaluation; //从起始状态到目标的最小估计值
private int misposition; //到目标的最小估计
private EightPuzzle parent; //当前状态的父状态

然后定义状态初始化信息:

          /**
* 求f(n) = g(n)+h(n);
* 初始化状态信息
* @param target
*/
public void init(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());
}

如果能够找到目标状态,将会通过parent属相找到路径并输出。

0X03 代码实现

 import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Scanner; @SuppressWarnings("rawtypes")
public class EightPuzzle implements Comparable{
private int[] num = new int[9];
private int depth; //当前的深度即走到当前状态的步骤
private int evaluation; //从起始状态到目标的最小估计值
private int misposition; //到目标的最小估计
private EightPuzzle parent; //当前状态的父状态
public int[] getNum() {
return num;
}
public void setNum(int[] num) {
this.num = num;
}
public int getDepth() {
return depth;
}
public void setDepth(int depth) {
this.depth = depth;
}
public int getEvaluation() {
return evaluation;
}
public void setEvaluation(int evaluation) {
this.evaluation = evaluation;
}
public int getMisposition() {
return misposition;
}
public void setMisposition(int misposition) {
this.misposition = misposition;
}
public EightPuzzle getParent() {
return parent;
}
public void setParent(EightPuzzle parent) {
this.parent = parent;
} /**
* 判断当前状态是否为目标状态
* @param target
* @return
*/
public boolean isTarget(EightPuzzle target){
return Arrays.equals(getNum(), target.getNum());
} /**
* 求f(n) = g(n)+h(n);
* 初始化状态信息
* @param target
*/
public void init(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
*/
public boolean isSolvable(EightPuzzle target){
int reverse = 0;
for(int i=0;i<9;i++){
for(int j=0;j<i;j++){
if(num[j]>num[i])
reverse++;
if(target.getNum()[j]>target.getNum()[i])
reverse++;
}
}
if(reverse % 2 == 0)
return true;
return false;
}
@Override
public int compareTo(Object o) {
EightPuzzle c = (EightPuzzle) o;
return this.evaluation-c.getEvaluation();//默认排序为f(n)由小到大排序
}
/**
* @return 返回0在八数码中的位置
*/
public int getZeroPosition(){
int position = -1;
for(int i=0;i<9;i++){
if(this.num[i] == 0){
position = i;
}
}
return position;
}
/**
*
* @param open 状态集合
* @return 判断当前状态是否存在于open表中
*/
public int isContains(ArrayList<EightPuzzle> open){
for(int i=0;i<open.size();i++){
if(Arrays.equals(open.get(i).getNum(), getNum())){
return i;
}
}
return -1;
}
/**
*
* @return 小于3的不能上移返回false
*/
public boolean isMoveUp() {
int position = getZeroPosition();
if(position<=2){
return false;
}
return true;
}
/**
*
* @return 大于6返回false
*/
public boolean isMoveDown() {
int position = getZeroPosition();
if(position>=6){
return false;
}
return true;
}
/**
*
* @return 0,3,6返回false
*/
public boolean isMoveLeft() {
int position = getZeroPosition();
if(position%3 == 0){
return false;
}
return true;
}
/**
*
* @return 2,5,8不能右移返回false
*/
public boolean isMoveRight() {
int position = getZeroPosition();
if((position)%3 == 2){
return false;
}
return true;
}
/**
*
* @param move 0:上,1:下,2:左,3:右
* @return 返回移动后的状态
*/
public EightPuzzle moveUp(int move){
EightPuzzle temp = new EightPuzzle();
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;
return temp;
}
/**
* 按照八数码的格式输出
*/
public void print(){
for(int i=0;i<9;i++){
if(i%3 == 2){
System.out.println(this.num[i]);
}else{
System.out.print(this.num[i]+" ");
}
}
}
/**
* 反序列的输出状态
*/
public void printRoute(){
EightPuzzle temp = null;
int count = 0;
temp = this;
while(temp!=null){
temp.print();
System.out.println("----------分割线----------");
temp = temp.getParent();
count++;
}
System.out.println("步骤数:"+(count-1));
}
/**
*
* @param open open表
* @param close close表
* @param parent 父状态
* @param target 目标状态
*/
public void operation(ArrayList<EightPuzzle> open,ArrayList<EightPuzzle> close,EightPuzzle parent,EightPuzzle target){
if(this.isContains(close) == -1){
int position = this.isContains(open);
if(position == -1){
this.parent = parent;
this.init(target);
open.add(this);
}else{
if(this.getDepth() < open.get(position).getDepth()){
open.remove(position);
this.parent = parent;
this.init(target);
open.add(this);
}
}
}
} @SuppressWarnings("unchecked")
public static void main(String args[]){
//定义open表
ArrayList<EightPuzzle> open = new ArrayList<EightPuzzle>();
ArrayList<EightPuzzle> close = new ArrayList<EightPuzzle>();
EightPuzzle start = new EightPuzzle();
EightPuzzle target = new EightPuzzle(); //BufferedReader br = new BufferedReader(new FileReader("./input.txt") );
String lineContent = null;
int stnum[] = {2,1,6,4,0,8,7,5,3};
int tanum[] = {1,2,3,8,0,4,7,6,5};
int order = 0;
try {
BufferedReader br;
br = new BufferedReader(new FileReader("input.txt") );
while((lineContent=br.readLine())!=null){
String[] str = lineContent.split(",");
for(int i = 0 ;i<str.length;i++){
if(order==0)
stnum[i] = Integer.parseInt(str[i]);
else
tanum[i] = Integer.parseInt(str[i]);
}
order++;
}
} catch (NumberFormatException e) {
System.out.println("请检查输入文件的格式,例如:2,1,6,4,0,8,7,5,3 换行 1,2,3,8,0,4,7,6,5");
e.printStackTrace();
} catch (IOException e) {
System.out.println("当前目录下无input.txt文件。");
e.printStackTrace();
}
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);
}
int move;
//由best状态进行扩展并加入到open表中
//0的位置上移之后状态不在close和open中设定best为其父状态,并初始化f(n)估值函数
if(best.isMoveUp()){
move = 0;
EightPuzzle up = best.moveUp(move);
up.operation(open, close, best, target);
}
//0的位置下移之后状态不在close和open中设定best为其父状态,并初始化f(n)估值函数
if(best.isMoveDown()){
move = 1;
EightPuzzle up = best.moveUp(move);
up.operation(open, close, best, target);
}
//0的位置左移之后状态不在close和open中设定best为其父状态,并初始化f(n)估值函数
if(best.isMoveLeft()){
move = 2;
EightPuzzle up = best.moveUp(move);
up.operation(open, close, best, target);
}
//0的位置右移之后状态不在close和open中设定best为其父状态,并初始化f(n)估值函数
if(best.isMoveRight()){
move = 3;
EightPuzzle up = best.moveUp(move);
up.operation(open, close, best, target);
} }
}else
System.out.println("没有解,请重新输入。");
} }

A*算法解决八数码问题 Java语言实现的更多相关文章

  1. Java实现 蓝桥杯 算法提高 八数码(BFS)

    试题 算法提高 八数码 问题描述 RXY八数码 输入格式 输入两个33表格 第一个为目标表格 第二个为检索表格 输出格式 输出步数 样例输入 1 2 3 4 5 6 7 8 0 1 2 3 4 5 6 ...

  2. 【算法】BFS+哈希解决八数码问题

    15拼图已经有超过100年; 即使你不叫这个名字知道的话,你已经看到了.它被构造成具有15滑动砖,每一个从1到15上,并且所有包装成4乘4帧与一个瓦块丢失.让我们把丢失的瓷砖“X”; 拼图的目的是安排 ...

  3. c++ 启发式搜索解决八数码问题

    本文对八数码问题 启发式搜索 (C++)做了一点点修改 //fn=gn+hn #include<iostream> #include<queue> #include<st ...

  4. C语言:试探算法解决“八皇后”问题

    #include <stdio.h> #define N 4 int solution[N], j, k, count, sols; int place(int row, int col) ...

  5. 算法之水仙花数(Java语言)

    概述 在数论中,水仙花数(Narcissistic number),也被称为超完全数字不变数(pluperfect digital invariant, PPDI).自恋数.自幂数.阿姆斯壮数或阿姆斯 ...

  6. A*算法解决15数码问题_Python实现

    1问题描述 数码问题常被用来演示如何在状态空间中生成动作序列.一个典型的例子是15数码问题,它是由放在一个4×4的16宫格棋盘中的15个数码(1-15)构成,棋盘中的一个单元是空的,它的邻接单元中的数 ...

  7. A*算法(八数码问题)

    #include <iostream> #include <cstring> #include <vector> #include <cmath> #i ...

  8. day02<Java语言基础+>

    Java语言基础(常量的概述和使用) Java语言基础(进制概述和二,八,十六进制图解) Java语言基础(不同进制数据的表现形式) Java语言基础(任意进制到十进制的转换图解) Java语言基础( ...

  9. A*算法 -- 八数码问题和传教士过河问题的代码实现

    前段时间人工智能的课介绍到A*算法,于是便去了解了一下,然后试着用这个算法去解决经典的八数码问题,一开始写用了挺久时间的,后来试着把算法的框架抽离出来,编写成一个通用的算法模板,这样子如果以后需要用到 ...

随机推荐

  1. 【FFT】专题总结

    学了若干天终于学(bei)会了传说中的法法塔 感觉也没那么难用嘛 fft快速傅里叶变换 在大表课件上写就是解决高精乘的工具 其实很有理有据 fft就是用复数的折半引理优化两个多项式相乘的高端东西 他能 ...

  2. ubuntu LVM

    本文介绍下,在ubuntu中扩展LVM磁盘的具体方法,有需要的朋友参考下吧. 当LV空间利用率较大即将耗尽LV空间时,可以将一块新的磁盘或一块磁盘上的/空间加入LV中. 现在/ 空间如下: 代码示例: ...

  3. rhel及相关linux系统版本知识

    Rhel 此处Rhel非等同redhat哦,RedHat是红帽公司在1994年左右开发维护的linux桌面版本,在2004年左右红帽公司放弃redhat开始进军linux服务器版本开发,具体见下截图 ...

  4. Django 的 CSRF 保护机制(转)

    add by zhj:假设用户登录了网站A,而在网站B中有一个CSRF攻击标签,点击这个标签就会访问网站A,如果前端数据(包括sessionid)都放在本地存储的话, 当在网站B点击CSRF攻击标签时 ...

  5. [翻译]创建ASP.NET WebApi RESTful 服务(9)

    一旦成功的发布API后,使用者将依赖于你所提供的服务.但是变更总是无法避免的,因此谨慎的制定ASP.NET Web API的版本策略就变得非常重要.一般来说,新的功能需要无缝的接入,有时新老版本需要并 ...

  6. <转载>linux下内存泄露查找、BUG调试

    先收藏着,抽空好好看看:http://www.ibm.com/developerworks/cn/linux/l-pow-debug/ 简介 调试程序有很多方法,例如向屏幕上打印消息,使用调试器,或者 ...

  7. 移动端页面的head头部内容

  8. lght oj 1257 - Farthest Nodes in a Tree (II) (树dp)

    题目链接:http://www.lightoj.com/volume_showproblem.php?problem=1257 跟hdu2196一样,两次dfs //#pragma comment(l ...

  9. Gym 101064 D Black Hills golden jewels (二分)

    题目链接:http://codeforces.com/gym/101064/problem/D 问你两个数组合相加的第k大数是多少. 先sort数组,二分答案,然后判断其正确性(判断过程是枚举每个数然 ...

  10. [源码分享] HIVE表数据量统计&邮件

    概要: 计算HIVE BI库下每天数据表总大小及增量 输出: 总大小:xxxG 日同比新增数据量:xxxG 周同比新增数据量:xxxG 月同比新增数据量:xxxG 总表数:xxx 日新增表数:xxx ...