题目描述
有以下的表格,然后每次与空白表格相邻的位置可以与其位置进行交换,求进行交换过程中的路径。
思路
这种题目可以使用A*算法来解决,具体的复杂度要看估价函数。
我这里使用了两种估价函数,虽然整体还是很垃圾,但是能够明显的感觉到不同估价函数对运行时间的影响。
另外就是,实现的算法中没有保存交换路径,留待解决。
具体的实现,我把表格封装成了一个对象,其中封装了移动,估价函数这些操作。
然后留给其他的就只是一个循环,不停的判断是否在OPEN表和CLOSED表中以及是否已经到达了结果 的位置。
可以继续优化的点,这个里面用到了排序,可以自己去实现一个排序列表,然后就不用每次都要进行排序,只需要根据相应的操作进行插入删除就好,这样应该还能再优化不少。
最后关于这两个例子都能运行出来结果,8数码应该几步就行,15数码我跑了40分钟,贼垃圾。
代码
8数码问题
package work;
import org.junit.Test;
import java.lang.reflect.Array;
import java.util.*;
/**
* 8数码问题
* @author modev
* @date 2020/9/30 15:07
*/
public class EightDigitalProblem {
@Test
public void test() {
Digital digital = new Digital(new int[][] {{2,8,3}, {1,6,4}, {7,0,5} });
Digital target = new Digital(new int[][] {{1,2,3}, {8,0,4}, {7,6,5} });
digital.setCount(target);
digital.x = 2;
digital.y = 1;
target.flag = 0;
target.x = 1;
target.y = 1;
start(digital, target);
}
public void start(Digital digital, Digital target) {
LinkedList<Digital> open = new LinkedList<>();
Set<Digital> closed = new HashSet<>();
open.addFirst(digital);
int count = 0;
while (!open.isEmpty() && count < 30) {
count++;
Digital node = open.pollFirst();
if (node.flag == 0 || target.equals(node)) {
System.out.println(closed);
System.out.println(node);
System.out.println("结束" + "\t 一共进行了:" + count +"次");
break;
}
if (closed.contains(node)) { continue; }
Digital[] tmp = {node.up(), node.down(), node.left(), node.right()};
for (Digital d : tmp) {
if (d == null) { continue; }
if (!closed.contains(d) && !open.contains(d)) {
d.setCount(target);
open.add(d);
}
}
//排序
Collections.sort(open);
closed.add(node);
// System.out.println(open);
}
}
}
/**
* 节点工具类
*/
class Digital implements Comparable<Digital>{
/**
* 存储表格
*/
int[][] board;
/**
* 存储和标准结果的差距
*/
int flag;
/**
* 统计是第几步了
*/
int step;
/**
* 存储空格的位置
*/
int x;
int y;
Digital() {
board = new int[3][3];
flag = 8;
x = 0;
y = 0;
}
Digital(int[][] board) {
this.board = board;
}
/**
* 获取当前状态向上移动一位的结果
* @return 新的对象,如果不存在则为null
*/
Digital up() {
if (x == 0) { return null; }
Digital tmp = new Digital();
for (int i = 0; i < 3; i++) {
System.arraycopy(this.board[i], 0, tmp.board[i], 0, 3);
}
tmp.board[x][y] = tmp.board[x-1][y];
tmp.board[x-1][y] = 0;
tmp.x = this.x-1;
tmp.y = this.y;
tmp.step = this.step+1;
return tmp;
}
/**
* 向下
* @return
*/
Digital down() {
if (x == 2) { return null; }
Digital tmp = new Digital();
for (int i = 0; i < 3; i++) {
System.arraycopy(this.board[i], 0, tmp.board[i], 0, 3);
}
tmp.board[x][y] = tmp.board[x+1][y];
tmp.board[x+1][y] = 0;
tmp.x = this.x+1;
tmp.y = this.y;
tmp.step = this.step+1;
return tmp;
}
/**
* 向左
* @return
*/
Digital left() {
if (y == 0) { return null; }
Digital tmp = new Digital();
for (int i = 0; i < 3; i++) {
System.arraycopy(this.board[i], 0, tmp.board[i], 0, 3);
}
tmp.board[x][y] = tmp.board[x][y-1];
tmp.board[x][y-1] = 0;
tmp.x = this.x;
tmp.y = this.y-1;
tmp.step = this.step+1;
return tmp;
}
/**
* 向右
* @return
*/
Digital right() {
if (y == 2) { return null; }
Digital tmp = new Digital();
for (int i = 0; i < 3; i++) {
System.arraycopy(this.board[i], 0, tmp.board[i], 0, 3);
}
tmp.board[x][y] = tmp.board[x][y+1];
tmp.board[x][y+1] = 0;
tmp.x = this.x;
tmp.y = this.y+1;
tmp.step = this.step+1;
return tmp;
}
/**
* 设置其与标准状态的差距的值
* @param target 标准状态
*/
void setCount(Digital target) {
int count = 0;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (this.board[i][j] != target.board[i][j]) {
count++;
}
}
}
this.flag = count;
}
/**
* 重写equals函数,只要两个对象中数据一样就相同
* @param o 要判断的对象
* @return 判断结果
*/
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Digital digital = (Digital) o;
boolean res = flag == digital.flag;
for (int i = 0; i < 3 && res; i++) {
res = Arrays.equals(board[i], digital.board[i]);
}
return res;
}
/**
* 重写hashCode函数,只要表格相同,得到的hash值就相同
* @return
*/
@Override
public int hashCode() {
int result = Arrays.hashCode(board[0]) * 2;
result += Arrays.hashCode(board[1]) * 3 + Arrays.hashCode(board[2]) * 7;
return result;
}
/**
* 因为要根据flag的值进行排序,所以就实现接口,重写函数
* @param o 要对比的对象
* @return 大于返回1,等于返回0,小于返回-1
*/
@Override
public int compareTo(Digital o) {
return Integer.compare(this.flag+this.step, o.flag+o.step);
}
@Override
public String toString() {
return "Digital{" +
"board=" + Arrays.toString(board[0]) + ", " + Arrays.toString(board[1])+ ", " + Arrays.toString(board[2]) +
", flag=" + flag +
", step=" + step +
", x=" + x +
", y=" + y +
'}';
}
}
15数码问题
package work;
import org.junit.Test;
import java.util.*;
/**
* @author modev
* @date 2020/10/6 14:16
*/
public class FifteenDigitalProblem {
@Test
public void test() {
FifteenDigital digital = new FifteenDigital(new int[][] {{11,9,4,15}, {1,3,0,12}, {7,5,8,6},{13,2,10,14} });
FifteenDigital target = new FifteenDigital(new int[][] {{1,2,3,4}, {5,6,7,8}, {9,10,11,12},{13,14,15,0} });
digital.setCount(target);
digital.x = 1;
digital.y = 2;
target.flag = 0;
target.x = 3;
target.y = 3;
start(digital, target);
}
public void start(FifteenDigital digital, FifteenDigital target) {
LinkedList<FifteenDigital> open = new LinkedList<>();
Set<FifteenDigital> closed = new HashSet<>();
open.addFirst(digital);
int count = 0;
while (!open.isEmpty()) {
count++;
FifteenDigital node = open.pollFirst();
if (node.flag == 0 || target.equals(node)) {
System.out.println(closed);
System.out.println(node);
System.out.println("结束" + "\t 一共进行了:" + count +"次");
break;
}
if (closed.contains(node)) { continue; }
FifteenDigital[] tmp = {node.up(), node.down(), node.left(), node.right()};
for (FifteenDigital d : tmp) {
if (d == null) { continue; }
if (!closed.contains(d) && !open.contains(d)) {
d.setCount(target);
open.add(d);
}
}
//排序
Collections.sort(open);
closed.add(node);
System.out.println(count);
}
}
}
/**
* 节点工具类
*/
class FifteenDigital implements Comparable<FifteenDigital>{
/**
* 存储表格
*/
int[][] board;
/**
* 存储和标准结果的差距
*/
int flag;
/**
* 统计是第几步了
*/
int step;
/**
* 存储空格的位置
*/
int x;
int y;
FifteenDigital() {
board = new int[4][4];
flag = 15;
x = 0;
y = 0;
}
FifteenDigital(int[][] board) {
this.board = board;
}
/**
* 获取当前状态向上移动一位的结果
* @return 新的对象,如果不存在则为null
*/
FifteenDigital up() {
if (x == 0) { return null; }
FifteenDigital tmp = new FifteenDigital();
for (int i = 0; i < 4; i++) {
System.arraycopy(this.board[i], 0, tmp.board[i], 0, 4);
}
tmp.board[x][y] = tmp.board[x-1][y];
tmp.board[x-1][y] = 0;
tmp.x = this.x-1;
tmp.y = this.y;
tmp.step = this.step+1;
return tmp;
}
/**
* 向下
* @return
*/
FifteenDigital down() {
if (x == 3) { return null; }
FifteenDigital tmp = new FifteenDigital();
for (int i = 0; i < 4; i++) {
System.arraycopy(this.board[i], 0, tmp.board[i], 0, 4);
}
tmp.board[x][y] = tmp.board[x+1][y];
tmp.board[x+1][y] = 0;
tmp.x = this.x+1;
tmp.y = this.y;
tmp.step = this.step+1;
return tmp;
}
/**
* 向左
* @return
*/
FifteenDigital left() {
if (y == 0) { return null; }
FifteenDigital tmp = new FifteenDigital();
for (int i = 0; i < 4; i++) {
System.arraycopy(this.board[i], 0, tmp.board[i], 0, 4);
}
tmp.board[x][y] = tmp.board[x][y-1];
tmp.board[x][y-1] = 0;
tmp.x = this.x;
tmp.y = this.y-1;
tmp.step = this.step+1;
return tmp;
}
/**
* 向右
* @return
*/
FifteenDigital right() {
if (y == 3) { return null; }
FifteenDigital tmp = new FifteenDigital();
for (int i = 0; i < 4; i++) {
System.arraycopy(this.board[i], 0, tmp.board[i], 0, 4);
}
tmp.board[x][y] = tmp.board[x][y+1];
tmp.board[x][y+1] = 0;
tmp.x = this.x;
tmp.y = this.y+1;
tmp.step = this.step+1;
return tmp;
}
/**
* 设置其与标准状态的差距的值
* @param target 标准状态
*/
void setCount(FifteenDigital target) {
this.flag = setManhattanDistanceCount(target);
}
/**
* 简单的计算有几个不同的数字
* @param target
* @return 不同数字的数量
*/
int setSimpleCount(FifteenDigital target) {
int count = 0;
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
if (this.board[i][j] != target.board[i][j]) {
count++;
}
}
}
return count;
}
/**
* 使用曼哈顿距离计算差值
* @param target
* @return 曼哈顿距离下的结果
*/
int setManhattanDistanceCount(FifteenDigital target) {
int count = 0;
int[][] maps = new int[16][2];
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
maps[target.board[i][j]][0] = i;
maps[target.board[i][j]][1] = j;
}
}
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
int tmp = Math.abs(maps[this.board[i][j]][0] - i) + Math.abs(maps[this.board[i][j]][1] - j);
count += tmp;
}
}
return count;
}
/**
* 重写equals函数,只要两个对象中数据一样就相同
* @param o 要判断的对象
* @return 判断结果
*/
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
FifteenDigital digital = (FifteenDigital) o;
boolean res = flag == digital.flag;
for (int i = 0; i < 4 && res; i++) {
res = Arrays.equals(board[i], digital.board[i]);
}
return res;
}
/**
* 重写hashCode函数,只要表格相同,得到的hash值就相同
* @return
*/
@Override
public int hashCode() {
int result = Arrays.hashCode(board[0]) * 2;
result += Arrays.hashCode(board[1]) * 3 + Arrays.hashCode(board[2]) * 7;
result += Arrays.hashCode(board[3]) * 11;
result = Integer.hashCode(result) * 17;
return result;
}
/**
* 因为要根据flag的值进行排序,所以就实现接口,重写函数
* @param o 要对比的对象
* @return 大于返回1,等于返回0,小于返回-1
*/
@Override
public int compareTo(FifteenDigital o) {
return Integer.compare(this.flag+this.step, o.flag+o.step);
}
@Override
public String toString() {
return "FifteenDigital{" +
"board=" + Arrays.toString(board[0]) + ", " + Arrays.toString(board[1])+ ", " + Arrays.toString(board[2]) +", " + Arrays.toString(board[3]) +
", flag=" + flag +
", step=" + step +
", x=" + x +
", y=" + y +
'}';
}
}