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. 第一百九十四天 how can I坚持

    该挺妈妈话的,不该买可乐,该熬点粥喝,肚子疼,救我. 好像每天都一样,每天都在重复.. 哎.. 对了,买了点花种子,铜钱草,牡丹.玫瑰.还买了棵小多肉. 还有,老妈把咸菜给邮过来了,有点期待啊. 连续 ...

  2. 第二百三十四天 how can I 坚持

    今天果然不负众望,下了一天的雪啊,挺好. 今天把花搞了下,都弄花盆里了,希望不会就这么挂掉.八千代,绿萝,还有小叶元宝. 中午喝了点酒,没感觉. 过两天气温就零下十多度了,该咋办啊,最怕冬天.家里现在 ...

  3. 转】从源代码剖析Mahout推荐引擎

    原博文出自于: http://blog.fens.me/mahout-recommend-engine/ 感谢! 从源代码剖析Mahout推荐引擎 Hadoop家族系列文章,主要介绍Hadoop家族产 ...

  4. cocos2d-x 3.2 DrawNode 绘图API

    关于Cocos2d-x 3.x 版本的绘图方法有两种: 1.使用DrawNode类绘制自定义图形. 2.继承Layer类重写draw()方法. 以上两种方法都可以绘制自定义图形,根据自己的需要选择合适 ...

  5. MYSQL数据库重点:流程控制语句

    1.BEGIN ... END复合语句:包含多个语句.statement_list 代表一个或多个语句的列表.statement_list之内每个语句都必须用分号(:)来结尾. [begin_labe ...

  6. ACM之递推递归

    Hdu 2569 突破蝙蝠的包围,yifenfei来到一处悬崖面前,悬崖彼岸就是前进的方向,好在现在的yifenfei已经学过御剑术,可御剑轻松飞过悬崖. 现在的问题是:悬崖中间飞着很多红,黄,蓝三种 ...

  7. js date string parse

    function dateParse(dStr){ //var dStr = '2016-1-26 0:7:14'; var d = dStr.split(' ')[0].split('-'); va ...

  8. (博弈论)hdoj 1525 Euclid's Game

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1525 题目大意:Stan和Ollie在玩一个游戏,a和b两个数字,每次可以选择较大的数减去较小的数的若 ...

  9. PowerDesigner反向生成Mysql数据原型

    PowerDesigner反向生成Mysql数据原型 注意事项: (1)JVM 要32位的. (2)需配置JAVA_HOME环境变量指向所需JVM. (3)需配置CLASSPATH环境变量执行 MyS ...

  10. 基于Emgu cv的图像拼接(转)

    分类: 编程 C# Emgu cv Stitching 2012-10-27 11:04 753人阅读 评论(1) 收藏 举报 在新版本的Emgu cv中添加了Emgu.CV.Stitching,这极 ...