思路:
先要解决下列问题:
如何表示节点?
如何扩展节点?
如何判断找到了目标节点?
最终路径是什么?
我设计了如下的类 :
EightPuzzle(算法类,有深度优先算法,宽度优先算法,A启发式搜索,打印结果的方法等,内部有open,closed表,
目标状态,初始状态)
Node (节点类,内部包含指向父节点的引用 parent,和指向子节点的引用 Children 是一个集合,fn,dn
wn 并实现了Comparable 接口,支持排序)
Chart (8数码类,继承自Node类,包含计算 不再位数目的方法,上下左右移动的方法,扩展节点的方法,
判断是否为父节点的方法(递归实现),打印的方法)
Position (位置类,包含位置坐标和位于其上的数字,0代表空格)每一个chart都有9个position
Graph (图类,用于保存所有节点)
如果不在位数目为0 则认为找到了目标节点。
通过移动空格 生成不同的Chart 就是子节点
最终路径 closed表中的节点。
在这些问题解答之后,可以写一个宽度优先算法,然后在宽度优先算法的基础上,写A启发式搜索算法。
参见教参(《人工智能》马少平,朱小燕 编著)
EightPuzzle 类
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
/**
* @author Sunmoonone
*
*/
enum Algorithms {
BreadthFirst, DepthFirst, AHeuristic
}
public class EightPuzzle {
Graph graph;
Node goal, initial;
ArrayList<Node> openList, closedList;
final int fail = 0, success = 1;
Algorithms algor = Algorithms.AHeuristic;
// ;
/**
*
*/
public EightPuzzle(int[] numForGoal, int[] numForInitial, int algorithmsType) {
this.goal = new Chart(numForGoal);
this.initial = new Chart(numForInitial);
this.openList = new ArrayList<Node>();
this.closedList = new ArrayList<Node>();
graph = new Graph();
System.out.println("Final Chart:");
((Chart) this.goal).print();
System.out.println();
System.out.println("Initial Chart:");
((Chart) this.initial).print();
System.out.println();
switch (algorithmsType) {
case 0:
this.algor = Algorithms.BreadthFirst;
this.BreadthFirst();
break;
/*
case 5:
this.algor = Algorithms.DepthFirst;
//this.DepthFirst();
break;
*/
case 1:
this.algor = Algorithms.AHeuristic;
this.AHeuristic();
break;
default:
this.algor = Algorithms.AHeuristic;
this.AHeuristic();
break;
}
}
/**
* Bread First Search
* @return
*/
public int BreadthFirst() {
System.out.println("Bread First Search");
graph.nodes.clear();
this.closedList.clear();
this.openList.clear();
graph.nodes.add(this.initial);
this.openList.add(this.initial);
System.out.println("add initial node to open list");
Node transNode;
while (true) {
if (this.openList.isEmpty()){
System.out.println("open list is empty");
return fail;
}
transNode = this.openList.get(0);
System.out.println("get first node from open list:");
transNode.print();
if (hit(transNode)){
System.out.println("Succeed");
return success;
}
this.closedList.add(transNode);
this.openList.remove(transNode);
//expand node
transNode.expandNode();
System.out.println("expand node:");
Iterator<Node> iter=transNode.children.iterator();
while(iter.hasNext()){
Node node=iter.next();
//modify parent reference of children
node.parent=transNode;
node.print();
}
this.graph.AddNodes(transNode.children);
//append nodes to the open list
//which are not contained the open list or the closed list
appendOpenList(transNode.children);
}
}
/**
* Depth First Search
* @return
*/
public int DepthFirst() {
return fail;
}
/**
* A Heuristic search
* @return
*/
public int AHeuristic() {
System.out.println("A Heuristic search");
//graph.nodes.clear();
this.closedList.clear();
this.openList.clear();
//graph.nodes.add(this.initial);
this.openList.add(this.initial);
this.initial.fn=0+Chart.nonSuited((Chart)this.initial, (Chart)this.goal);
System.out.println("add initial node to open list");
Node transNode;
while (true) {
if (this.openList.isEmpty()){
System.out.println("open list is empty");
return fail;
}
transNode = this.openList.get(0);
System.out.println("get first node from open list:");
transNode.print();
if (hit(transNode)){
System.out.println("Succeed");
return success;
}
this.closedList.add(transNode);
this.openList.remove(transNode);
//expand node
transNode.expandNode();
//calculate f(n,mi),f(n,mj),f(n,ml) and modify references
modifyReference(transNode,transNode.children);
//sort by fn of nodes
sort(this.openList);
System.out.println("open list sorted by fn:");
printFns();
}
}
private void printFns() {
Iterator<Node> iter=this.openList.iterator();
while(iter.hasNext()){
System.out.print(iter.next().fn+" ");
}
System.out.println();
}
private void sort(ArrayList<Node> openList2) {
Collections.sort(openList2);
}
private void modifyReference(Node transNode, ArrayList<Node> children) {
Iterator<Node> iter=children.iterator();
while(iter.hasNext()){
Node node=iter.next();
//add mj to open list and set parent reference to transNode
if(!this.openList.contains(node)&&!this.closedList.contains(node)){
this.openList.add(node);
node.parent=transNode;
node.dn=node.parent.dn+1;
node.wn=Chart.nonSuited((Chart)node,(Chart)this.goal);
node.fn=node.dn+node.wn;
System.out.println("append:mj");
}
//mk
else if(this.openList.contains(node)){
if(transNode.dn+1+node.wn<node.fn){
node.parent=transNode;
node.fn=transNode.dn+1+node.wn;
node.dn=transNode.dn+1;
System.out.println("modified fn and referece:mk");
}
}
//ml
else if(this.closedList.contains(node)){
if(transNode.dn+1+node.wn<node.fn){
node.fn=transNode.dn+1+node.wn;
node.dn=transNode.dn+1;
node.parent=transNode;
this.openList.add(node);
this.closedList.remove(node);
System.out.println("modified fn and referece and add it back to open list:ml");
}
}
}
}
/**
* add collection of nodes to the open list
* operation is different depending on the
* different Algorithms
* @param nodes
*/
private void appendOpenList(ArrayList<Node> nodes) {
System.out.println("append nodes to open list:");
Iterator<Node> iter = nodes.iterator();
while (iter.hasNext()) {
if (this.algor == Algorithms.BreadthFirst) {
Node temp = iter.next();
if (!this.openList.contains(temp)
&& !this.closedList.contains(temp)) {
//append node to the end of the open list
this.openList.add(temp);
temp.print();
}
}
}
}
private boolean hit(Node transNode) {
if (Chart.nonSuited((Chart) transNode, (Chart) goal) == 0) {
return true;
}
return false;
}
/**
* print search path
*/
public void print() {
System.out.println("Initial Chart:");
((Chart) this.initial).print();
System.out.println();
System.out.println("Final Chart:");
((Chart) this.goal).print();
System.out.println("---------------Search path---------------------");
Iterator<Node> iter = this.closedList.iterator();
while (iter.hasNext()) {
iter.next().print();
System.out.println();
}
System.out.println("-----------------Graph-------------------");
System.out.println("graph nodes count:"+this.graph.nodes.size());
this.graph.print();
}
}
import 和 class EightPuzzle
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
/**
* @author Sunmoonone
*
*/
enum Algorithms {
BreadthFirst, DepthFirst, AHeuristic
}
public class EightPuzzle {
Graph graph;
Node goal, initial;
ArrayList<Node> openList, closedList;
final int fail = 0, success = 1;
Algorithms algor = Algorithms.AHeuristic;
// ;
/**
*
*/
public EightPuzzle(int[] numForGoal, int[] numForInitial, int algorithmsType) {
this.goal = new Chart(numForGoal);
this.initial = new Chart(numForInitial);
this.openList = new ArrayList<Node>();
this.closedList = new ArrayList<Node>();
graph = new Graph();
System.out.println("Final Chart:");
((Chart) this.goal).print();
System.out.println();
System.out.println("Initial Chart:");
((Chart) this.initial).print();
System.out.println();
switch (algorithmsType) {
case 0:
this.algor = Algorithms.BreadthFirst;
this.BreadthFirst();
break;
/*
case 5:
this.algor = Algorithms.DepthFirst;
//this.DepthFirst();
break;
*/
case 1:
this.algor = Algorithms.AHeuristic;
this.AHeuristic();
break;
default:
this.algor = Algorithms.AHeuristic;
this.AHeuristic();
break;
}
}
/**
* Bread First Search
* @return
*/
public int BreadthFirst() {
System.out.println("Bread First Search");
graph.nodes.clear();
this.closedList.clear();
this.openList.clear();
graph.nodes.add(this.initial);
this.openList.add(this.initial);
System.out.println("add initial node to open list");
Node transNode;
while (true) {
if (this.openList.isEmpty()){
System.out.println("open list is empty");
return fail;
}
transNode = this.openList.get(0);
System.out.println("get first node from open list:");
transNode.print();
if (hit(transNode)){
System.out.println("Succeed");
return success;
}
this.closedList.add(transNode);
this.openList.remove(transNode);
//expand node
transNode.expandNode();
System.out.println("expand node:");
Iterator<Node> iter=transNode.children.iterator();
while(iter.hasNext()){
Node node=iter.next();
//modify parent reference of children
node.parent=transNode;
node.print();
}
this.graph.AddNodes(transNode.children);
//append nodes to the open list
//which are not contained the open list or the closed list
appendOpenList(transNode.children);
}
}
/**
* Depth First Search
* @return
*/
public int DepthFirst() {
return fail;
}
/**
* A Heuristic search
* @return
*/
public int AHeuristic() {
System.out.println("A Heuristic search");
//graph.nodes.clear();
this.closedList.clear();
this.openList.clear();
//graph.nodes.add(this.initial);
this.openList.add(this.initial);
this.initial.fn=0+Chart.nonSuited((Chart)this.initial, (Chart)this.goal);
System.out.println("add initial node to open list");
Node transNode;
while (true) {
if (this.openList.isEmpty()){
System.out.println("open list is empty");
return fail;
}
transNode = this.openList.get(0);
System.out.println("get first node from open list:");
transNode.print();
if (hit(transNode)){
System.out.println("Succeed");
return success;
}
this.closedList.add(transNode);
this.openList.remove(transNode);
//expand node
transNode.expandNode();
//calculate f(n,mi),f(n,mj),f(n,ml) and modify references
modifyReference(transNode,transNode.children);
//sort by fn of nodes
sort(this.openList);
System.out.println("open list sorted by fn:");
printFns();
}
}
private void printFns() {
Iterator<Node> iter=this.openList.iterator();
while(iter.hasNext()){
System.out.print(iter.next().fn+" ");
}
System.out.println();
}
private void sort(ArrayList<Node> openList2) {
Collections.sort(openList2);
}
private void modifyReference(Node transNode, ArrayList<Node> children) {
Iterator<Node> iter=children.iterator();
while(iter.hasNext()){
Node node=iter.next();
//add mj to open list and set parent reference to transNode
if(!this.openList.contains(node)&&!this.closedList.contains(node)){
this.openList.add(node);
node.parent=transNode;
node.dn=node.parent.dn+1;
node.wn=Chart.nonSuited((Chart)node,(Chart)this.goal);
node.fn=node.dn+node.wn;
System.out.println("append:mj");
}
//mk
else if(this.openList.contains(node)){
if(transNode.dn+1+node.wn<node.fn){
node.parent=transNode;
node.fn=transNode.dn+1+node.wn;
node.dn=transNode.dn+1;
System.out.println("modified fn and referece:mk");
}
}
//ml
else if(this.closedList.contains(node)){
if(transNode.dn+1+node.wn<node.fn){
node.fn=transNode.dn+1+node.wn;
node.dn=transNode.dn+1;
node.parent=transNode;
this.openList.add(node);
this.closedList.remove(node);
System.out.println("modified fn and referece and add it back to open list:ml");
}
}
}
}
/**
* add collection of nodes to the open list
* operation is different depending on the
* different Algorithms
* @param nodes
*/
private void appendOpenList(ArrayList<Node> nodes) {
System.out.println("append nodes to open list:");
Iterator<Node> iter = nodes.iterator();
while (iter.hasNext()) {
if (this.algor == Algorithms.BreadthFirst) {
Node temp = iter.next();
if (!this.openList.contains(temp)
&& !this.closedList.contains(temp)) {
//append node to the end of the open list
this.openList.add(temp);
temp.print();
}
}
}
}
private boolean hit(Node transNode) {
if (Chart.nonSuited((Chart) transNode, (Chart) goal) == 0) {
return true;
}
return false;
}
/**
* print search path
*/
public void print() {
System.out.println("Initial Chart:");
((Chart) this.initial).print();
System.out.println();
System.out.println("Final Chart:");
((Chart) this.goal).print();
System.out.println("---------------Search path---------------------");
Iterator<Node> iter = this.closedList.iterator();
while (iter.hasNext()) {
iter.next().print();
System.out.println();
}
System.out.println("-----------------Graph-------------------");
System.out.println("graph nodes count:"+this.graph.nodes.size());
this.graph.print();
}
}
Node类
class Node
class Node implements Comparable<Node>{
public static int count=0;
int id;
Node parent;
ArrayList<Node> children;
int fn=0;
int dn=0;
int wn=0;
public Node() {
this.id=count;
count++;
this.children = new ArrayList<Node>();
}
public void expandNode() {
}
public void print() {
}
/**
* whether a node is one of the parents of this node this method is used to
* verify a child node that will be added as child node of this node
*
* @param node
* @return true if nod is one of the parents of this node
*/
public boolean parentContains(Node node) {
return false;
}
public int compareTo(Node o) {
if(this.fn<o.fn)
return -1;
else if(this.fn==o.fn)
return 0;
return 1;
}
}
Chart类
class Chart
class Chart extends Node {
Position[] positions = new Position[9];
public Chart() {
super();
}
/**
* construct a Chart instance with 9 numbers
*
* @param numbers
* 9 numbers([0-8]) ,0 represents white space
*/
public Chart(int[] numbers) {
super();
Position pos;
int cursor = 0;
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++) {
//j is x coordinate, i is y coordinate
pos = new Position(j, i, numbers[cursor]);
this.positions[cursor] = pos;
cursor++;
}
}
/**
* print this chart
*/
public void print() {
int cursor = 0;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
System.out.print(this.positions[cursor].toString() + " ");
cursor++;
}
System.out.println();
}
System.out.println("id:"+this.id);
System.out.print("Parent:");
if(this.parent!=null)
System.out.print(this.parent.id);
System.out.println();
System.out.print("Children:");
Iterator<Node> iter=this.children.iterator();
while(iter.hasNext()){
System.out.print(iter.next().id+" ");
}
System.out.println();
System.out.println("-----");
}
/**
* creates and returns a copy of this object the new cloned object also has
* a copy of the data of this object
*/
public Chart clone() {
Chart newchart = new Chart();
//newchart.positions = (Position[]) this.positions.clone();
//System.arraycopy(this.positions, 0, newchart.positions, 0, this.positions.length);
//because Position is a class
for(int i=0;i<this.positions.length;i++){
newchart.positions[i]=(Position)this.positions[i].clone();
}
return newchart;
}
/**
* get index of a position by number placed in it.
*
* @param number
* number placed in this position
* @return get index of a position
*/
public int getPosition(int number) {
for (int i = 0; i < this.positions.length; i++) {
if (this.positions[i].getNum() == number) {
return i;
}
}
return -1;
}
/**
* get index of a position by coordinate
*
* @param x
* x coordinate
* @param y
* x coordinate
* @return index of a position
*/
public int getPosition(int x, int y) {
for (int i = 0; i < this.positions.length; i++) {
if (this.positions[i].getX() == x && this.positions[i].getY() == y) {
return i;
}
}
return -1;
}
public Chart moveLeft() {
//at the left edge
if (this.positions[this.getPosition(0)].getX() == 0) {
return null;
} else {
Chart newChart = this.clone();
newChart.switchPosition(newChart.getPosition(0), newChart.getPosition(0)-1);
return newChart;
}
}
public Chart moveRight() {
//at the right edge
if (this.positions[this.getPosition(0)].getX() == 2) {
return null;
} else {
Chart newChart = this.clone();
newChart.switchPosition(newChart.getPosition(0), newChart.getPosition(0)+1);
return newChart;
}
}
public Chart moveUp() {
//in the top line
if (this.positions[this.getPosition(0)].getY() == 0) {
return null;
} else {
Chart newChart = this.clone();
newChart.switchPosition(newChart.getPosition(0), newChart.getPosition(0)-3);
return newChart;
}
}
public Chart moveDown() {
//in the bottom line
if (this.positions[this.getPosition(0)].getY() == 2) {
return null;
} else {
Chart newChart = this.clone();
newChart.switchPosition(newChart.getPosition(0), newChart.getPosition(0)+3);
return newChart;
}
}
/**
* switch number displayed in these two positions of this chart
* but not the coordinate
* @param i index of position
* @param j index of position
*/
private void switchPosition(int i,int j){
int temp;
temp=this.positions[i].getNum();
this.positions[i].setNum(this.positions[j].getNum());
this.positions[j].setNum(temp);
}
/**
* expand this node
*/
public void expandNode() {
Chart chLeft, chRight, chUp, chDown;
chLeft = this.moveLeft();
chRight = this.moveRight();
chUp = this.moveUp();
chDown = this.moveDown();
if (chLeft != null && !this.parentContains(chLeft)) {
this.children.add(chLeft);
}
if (chRight != null && !this.parentContains(chRight)) {
this.children.add(chRight);
}
if (chUp != null && !this.parentContains(chUp)) {
this.children.add(chUp);
}
if (chDown != null && !this.parentContains(chDown)) {
this.children.add(chDown);
}
}
/**
* get count of non suited numbers
*
* @param trans
* transient chart
* @param goal
* destination
* @return
*/
public static int nonSuited(Chart trans, Chart goal) {
int result = 0;
for (int i = 0; i < goal.positions.length; i++) {
if (goal.positions[i].getNum() != trans.positions[i].getNum()) {
result++;
}
}
System.out.println("w(n)="+result);
return result;
}
public boolean parentContains(Node node) {
if (this.parent != null) {
if (this.parent.equals(node))
return true;
else
return this.parent.parentContains(node);
}
return false;
}
public boolean equals(Object o) {
Chart chart = (Chart) o;
if (this.positions.equals(chart.positions))
return true;
else
return false;
}
}
Position类
Code
class Position {
// coordinate
private int x, y;
public int getY() {
return y;
}
public int getX() {
return x;
}
/**
* number placed in this position 0 represents white space
*/
private int num;
/**
* get number placed in this position 0 represents white space
*/
public int getNum() {
return num;
}
/**
* set number placed in this position 0 represents white space
*/
public void setNum(int num) {
this.num = num;
}
/**
*
* @param x
* x coordinate
* @param y
* y coordinate
* @param number
* number placed in this position
*/
public Position(int x, int y, int number) {
this.x = x;
this.y = y;
this.num = number;
}
public Position() {
// TODO Auto-generated constructor stub
}
public Object clone(){
return new Position(this.getX(),this.getY(),this.getNum());
}
public String toString() {
if (this.getNum() == 0)
return " ";
return String.valueOf(this.getNum());
}
}
Graph类
class Graph
class Graph {
ArrayList<Node> nodes;
public Graph() {
nodes = new ArrayList<Node>();
}
/**
* add collections of nodes to this graph
*
* @param nodes
*/
public void AddNodes(ArrayList<Node> nodes) {
Iterator<Node> iter = nodes.iterator();
while (iter.hasNext()) {
this.nodes.add(iter.next());
}
}
public void print(){
Iterator<Node> iter=this.nodes.iterator();
while(iter.hasNext()){
iter.next().print();
}
}
}
程序入口类
Main
public class Main {
/**
* @param args
*/
public static void main(String[] args) {
try {
System.out
.println("Type numbers([0-8]) and Algorithms type in format 2 8 3 1 6 4 7 0 5,1 2 3 8 0 4 7 6 5,1");
System.out.println("The first 9 numbers are for the final state,the last integer is for Algorithms type.");
System.out.println("0:BreadthFirst; 1:AHeuristic.");
String str;
BufferedReader inStream = new BufferedReader(new InputStreamReader(
System.in));
str = inStream.readLine();
String[] strs = str.split(",");
String[] intstrs1 = strs[0].split(" ");
int[] ints1 = new int[9];
for (int i = 0; i < 9; i++) {
ints1[i] = Integer.parseInt(intstrs1[i]);
}
String[] intstrs2 = strs[1].split(" ");
int[] ints2 = new int[9];
for (int i = 0; i < 9; i++) {
ints2[i] = Integer.parseInt(intstrs2[i]);
}
int type = Integer.parseInt(strs[2]);
//
EightPuzzle ep = new EightPuzzle(ints1, ints2, type);
ep.print();
} catch (IOException e) {
System.out.println(e.getMessage());
}
}
}