这段时间看A*,然后就把经典问题八数码做了一遍。以前听朋友说过,自己实现的时候还是学到了不少东西。
八数码问题主要思想:
1.将3×3的table用一维字符串表示
2.一维字符串是共有9!个,因此用hash来散列
3.把问题转换为寻路问题,起点是初始状态,终点是目标状态,每移一下,相当于走了一步
我觉得八数码问题当中的估计函数(启发函数)是一个亮点:我自己想到的是用当前的字符串与 目标字符串比较,h=不在位的数。这个启发函数虽然不是很好,但是还是能做出来的。
同样,八数码解的判定也是个亮点:逆序数。转成一维以后,计算出不包含0的逆序数,然后奇偶性判断。这个是网上找到的方法,觉得非常牛,赞一下。
这些是自己的心得。附上代码:
import java.util.Date; public class Mainx { public static void main(String[] argvv){ DigitTable table = new DigitTable("578064321","123456780"); Date dd = new Date(); boolean res =table.run(); if(!res){ System.out.println("无解"); return; } Long t = (new Date()).getTime()- dd.getTime(); table.printStep(); System.out.println("运行时间"+t+"毫秒"); } }
import java.util.Comparator;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.PriorityQueue;
import java.util.Set;
/**
* @author coolgo
*/
public class DigitTable {
private int stepx[] = { 0, 1, 0, -1 };// 上下左右交换
private int stepy[] = { 1, 0, -1, 0 };// 上下左右交换
private String startState;
private String endState;
private Hashtable<String, Node> tb = new Hashtable<String, Node>();
public DigitTable(String start, String end) {
startState = start;
endState = end;
}
public boolean run() {
if(!hasSolution()) return false;
NodeComparator cmp = new NodeComparator();
PriorityQueue<Node> open = new PriorityQueue<Node> (1000000,cmp);
Set<String> close = new HashSet<String>();
Node tmp = new Node(startState, endState, 0);
tmp.f = tmp.g + tmp.h;
open.add(tmp);
tb.put(tmp.state, tmp);
int t, zero;
int x, y, nxtx, nxty;
String cur, nxtStr;
Node top, nxt;
while (!open.isEmpty()) {
top = open.poll();
cur = top.state;
close.add(cur);
if (close.contains(endState))
break;
zero = cur.indexOf("0");
x = zero / 3;
y = zero % 3;
for (int i = 0; i < 4; i++) {
nxtx = x + stepx[i];
nxty = y + stepy[i];
if (nxtx >= 3 || nxtx < 0 || nxty < 0 || nxty >= 3)
continue;
t = nxtx * 3 + nxty;
nxtStr = getNxtStr(cur, t, zero);
if (tb.containsKey(nxtStr)) {
if (close.contains(nxtStr))
continue;
nxt = tb.get(nxtStr);
if (nxt.g > top.g + 1) {
nxt.g = top.g + 1;
nxt.f = nxt.g + nxt.h;
nxt.parent = top;
}
} else {
nxt = new Node(nxtStr, endState, top.g + 1);
nxt.parent = top;
tb.put(nxtStr, nxt);
open.add(nxt);
}
}
}
return true;
}
/**
* 通过逆序数判断,是否有解
* 两个互相可达的状态对应序列逆序数的奇偶性应该相同
* 1 2 3 4 5 6 8 7奇偶性为1
* 1 2 3 4 5 6 7 8奇偶性为0
* 所以两状态相互不可达
* @return
*/
private boolean hasSolution() {
return getReverseNum(startState)%2==getReverseNum(endState)%2;
}
private int getReverseNum(String str){
int rs=0;
char[] cs = str.toCharArray();
for(int i =0; i< 9; i++){
for(int j = 0; j <i; j++){
if((cs[j]<cs[i])&&(cs[i]!='0'&&cs[j]!='0'))rs++;
}
}
return rs;
}
public void printStep(){
System.out.println("状态数"+tb.size());
System.out.println("步骤数"+tb.get(endState).f);
Node cur = tb.get(endState);
while(!cur.parent.equals(cur)){
printMap(cur.state);
System.out.println("====");
cur = cur.parent;
}
}
private void printMap(String nxtStr) {
for (int i = 0; i < 9; i += 3)
System.out.println(nxtStr.substring(i, i + 3));
}
private String getNxtStr(String cur, int t, int zero) {
char[] cs = cur.toCharArray();
cs[zero] = cs[t];
cs[t] = '0';
return String.valueOf(cs);
}
/**
* 比较器
* @author coolgo
*/
public class NodeComparator implements Comparator<Node> {
@Override
public int compare(Node x, Node y) {
return x.f - y.f;
}
}
}
/**
* 每次变化后的状态
* @author coolgo
*/
public class Node {
/**
* f = g + h; h为估价函数
*/
public int f = 1 << 30;
public int g = 1 << 30;
public int h = 1 << 30;
public String state = "";
public Node parent = this;
public Node(String str, String endState, int i) {
state = str;
h= getH(state, endState);
g = i;
f = g+h;
}
/**
* 估计函数:不在本位的数
* @param cur
* @param endState
* @return
*/
private int getH(String cur, String endState) {
int x = 0;
for (int i = 0; i < 9; i++) {
x += (cur.charAt(i) == endState.charAt(i) ? 0 : 1);
}
return x;
}
}