同一个世界OL游戏 算法求解 改进版

阅读前准备

建议先看这篇文章同一个世界游戏 算法破解

改进思路

对于前篇文章(同一个世界游戏 算法破解)的Node类是通过Map<Integer, Integer>的对象来记录之前的走过的位置,这占用了大量的内存空间,检查的效率也低;节点的节点值等于父节点的值加本身的值(long childValue = parent.getValue() + (long) ),但是针对这个游戏(编码保存方式)而言,是否有可能有更好的方式
对以上两点考虑得到了更好的解决方式

重复位置检查

该游戏的状态是以二进制的方式保存的,经过的点对应的位标志为1,否则为0,也就是说任意一个节点的值实际上就已经保存了它所经过点,所以它的子节点只要检查节点值的二进制对应的位是否为1,即可知道是否走过该点

	// 未经过该点
	private boolean notPassed(long oldPosition, int index) {
		return (oldPosition >> index & 1) == 0;
	}

这样就节省了大量的时间和内存空间

节点值求和

有了上面的严谨检查,确保了子节点不会走过重复的点,这意味着一个节点值的二进制不会在相同的位上重复出现1的情况,也就是没有进位的情况,这样就可以把原来的加法运算改为或运算

long childValue = parent.getValue() | getValue(childIndex);

但是原本的加法也不可能会出现进位的情况,我想这并没有减少多少时间

其它问题

PathUtils类生成路径的时候可能是闭合路径但是还没有达到指定的路径深度,会造成死循环
重构NodeTree类的代码

代码

Node.java

public class Node {

	private Node parent; // 父节点
	private long value; // 当前路径值
	private int row; // 当前节点的行
	private int col; // 当前节点的列
	private int index; // 起点序号
	
	public Node getParent() {
		return parent;
	}
	public void setParent(Node parent) {
		this.parent = parent;
	}
	public long getValue() {
		return value;
	}
	public void setValue(long value) {
		this.value = value;
	}
	public int getRow() {
		return row;
	}
	public void setRow(int row) {
		this.row = row;
	}
	public int getCol() {
		return col;
	}
	public void setCol(int col) {
		this.col = col;
	}
	public int getIndex() {
		return index;
	}
	public void setIndex(int index) {
		this.index = index;
	}
	
}

NodeTree.java

import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

public class NodeTree {

	private int[][] state; // 盘面
	private int row, col; // 二维数组行、列
	private long target; // 目标状态值
	private Node[] solveNodes; // 解决路径
	private List<Map<Long, Node>> nodes; // 所有路径
	private Deque<Node> deque; // 可能路径
	private long realSum; // 实际路径数量

	public NodeTree(int[][] state, int[][] startPoint, long target) {
		this.state = state;
		this.target = target;
		this.row = state.length;
		this.col = state[0].length;
		nodes = new ArrayList<>(startPoint.length);
		for (int i = 0; i < startPoint.length; i++) {
			nodes.add(new HashMap<>());
		}
		deque = new LinkedList<>();
		// 起点
		for (int i = 0; i < startPoint.length; i++) {
			int index = getIndex(startPoint[i][0], startPoint[i][1]);
			long value = getValue(index);

			Node node = new Node();
			node.setParent(null);
			node.setValue(value);
			node.setRow(startPoint[i][0]);
			node.setCol(startPoint[i][1]);
			node.setIndex(i);
			deque.push(node);
			nodes.get(i).put(value, node);
			realSum++;
		}
		solveNodes = new Node[startPoint.length];
	}
	
	// 搜索
	public void search(boolean dfs) {
		long startTime = System.currentTimeMillis();
		Node parent, childNode;

		while (!deque.isEmpty()) {

			parent = deque.poll();
//			System.out.println("nodeValue : " + parent.getValue());
			
			for (int i = 0; i < 4; i++) {
				if (!solved(parent)) {
					childNode = nextStep(parent, i);
					if (childNode != null) {
						realSum++;
						if (dfs) {
							deque.offerFirst(childNode);
						} else {
							deque.offerLast(childNode);
						}
					}
				} else {
					printSearchInfo(startTime, "success");
					printPath();
					return;
				}
			}

		}

		printSearchInfo(startTime, "fail");
	}
	
	private final int[] offsetRow = {-1, 1, 0, 0}; // 行的偏移量
	private final int[] offsetCol = {0, 0, -1, 1}; // 列的偏移量

	// 走一步
	private Node nextStep(Node parent, int direction) {
		int childRow = parent.getRow() + offsetRow[direction];
		int childCol = parent.getCol() + offsetCol[direction];
		int childIndex = getIndex(childRow, childCol);

		if (0 <= childRow && childRow < this.row
				&& 0 <= childCol && childCol < this.col
				&& state[childRow][childCol] != 0
				&& notPassed(parent.getValue(), childIndex)) {

				long childValue = parent.getValue() | getValue(childIndex);
				Node childNode = new Node();
				childNode.setParent(parent);
				childNode.setValue(childValue);
				childNode.setRow(childRow);
				childNode.setCol(childCol);
				childNode.setIndex(parent.getIndex());
				nodes.get(parent.getIndex()).put(childValue, childNode);
				return childNode;

		}
		return null;
	}

	// 搜索完成后的信息
	private void printSearchInfo(long startTime, String result) {
		System.out.println("搜索时间 : " + (System.currentTimeMillis() - startTime) + "毫秒");
		System.out.println(result);
		long sum = 0;
		for (int i = 0; i < nodes.size(); i++) {
			sum += nodes.get(i).size();
		}
		System.out.println("搜索路径数 : " + sum);
		System.out.println("真实搜索路径数 : " + realSum);
	}
	
	// 打印路径
	private void printPath() {
		for (int i = 0; i < solveNodes.length; i++) {
			System.out.println("--------------------");
			Node node = solveNodes[i];
			while (node != null) {
				System.out.println(node.getRow() + " " + node.getCol());
				node = node.getParent();
			}
		}
	}

	// 检查是否是符合目标的路径
	private boolean solved(Node node) {
		long nextTarget = target;
		for (int i = 0; i < solveNodes.length && node != null; i++) {
			nextTarget = nextTarget ^ node.getValue();
			solveNodes[i] = node;
			if (nextTarget == 0) {
				return true;
			} else if (nextTarget > 0) {
				node = nodes.get((node.getIndex() + 1) % nodes.size()).get(nextTarget);
			} else {
				break;
			}
		}
		return false;
	}
	
	// 二维数组对应的一维数组索引
	private int getIndex(int row, int col) {
		return row * this.col + col;
	}

	// 2的index次方
	private long getValue(int index) {
		return (long) Math.pow(2, index);
	}
	
	// 未经过该点
	private boolean notPassed(long oldPosition, int index) {
		return (oldPosition >> index & 1) == 0;
	}
}

PathUtils.java

/**
 * 路径工具类
 * @author L
 *
 */
public class PathUtils {

	/**
	 * 生成路径
	 * @param state 盘面
	 * @param depth 路径深度
	 * @param startPoint 起点
	 * @return 路径
	 */
	public static int[][] createPath(int[][] state, int[] depth, int[][] startPoint) {
		
		int[][] path = new int[depth.length][];
		
		for (int i = 0; i < startPoint.length; i++) {
			path[i] = new int[depth[i]];
			int startRow = startPoint[i][0];
			int startCol = startPoint[i][1];
			int k = 0;
			path[i][k++] = startRow * state[i].length + startCol;
			while (k < depth[i]) {
				double random = Math.random();
				if (random < 0.25 && positionCheck(state, path[i], startRow - 1, startCol)) {
					path[i][k++] = --startRow * state[i].length + startCol;
				} else if (random < 0.5 && positionCheck(state, path[i], startRow + 1, startCol)) {
					path[i][k++] = ++startRow * state[i].length + startCol;
				} else if (random < 0.75 && positionCheck(state, path[i], startRow, startCol - 1)) {
					path[i][k++] = startRow * state[i].length + --startCol;
				} else if (positionCheck(state, path[i], startRow, startCol + 1)) {
					path[i][k++] = startRow * state[i].length + ++startCol;
				}
				if (isClosedPath(state, path[i], startRow, startCol)) {
					break;
				}
			}
		}
		
		return path;
	}

	// 该节点是否是闭合路径
	private static boolean isClosedPath(int[][] state, int[] path, int row, int col) {
		int[] offsetRow = {-1, 1, 0, 0}; // 行的偏移量
		int[] offsetCol = {0, 0, -1, 1}; // 列的偏移量
		int count = 0;
		for (int i = 0; i < 4; i++) {
			if (positionCheck(state, path, row + offsetRow[i], col + offsetCol[i])) {
				++count;
			}
		}
		return count == 0;
	}

	// 配合createPath方法使用,防止再次访问已访问过的位置
	private static boolean positionCheck(int[][] state, int[] path, int row, int col) {
		if (0 <= row && row < state.length
				&& 0 <= col && col < state[0].length
				&& state[row][col] != 0) {
			int index = row * state[0].length + col;
			for (int i = 0; i < path.length; i++) {
				if (index == path[i]) {
					return false;
				}
			}
			return true;
		}
		return false;
	}
	
	// 配合createPath使用
	public static long getPathValue(int[][] path) {
		long value = 0;
		for (int i = 0; i < path.length; i++) {
			for (int j = 0; j < path[i].length; j++) {
				value ^= (long) Math.pow(2, path[i][j]);
			}
		}
		return value;
	}
	
	// 盘面值
	public static long getStateValue(int[][] state, int v) {
		long value = 0;
		for (int i = 0; i < state.length; i++) {
			for (int j = 0; j < state[i].length; j++) {
				if (state[i][j] == v) {
					int index = i * state[i].length + j;
					value ^= (long) Math.pow(2, index);
				}
			}
		}
		return value;
	}
}

改进后的效率

测试条件

PC:i5处理器,8G内存
盘面大小:5行6列、6行6列
起点:对角线起点(路径数量最大)
搜索方式:深度优先

测试结果

改进前

5行6列

在这里插入图片描述
结果
在这里插入图片描述

6行6列

在这里插入图片描述
结果
在这里插入图片描述
跑了一段时间后就内存溢出了

改经后

5行6列

在这里插入图片描述
在这里插入图片描述

6行6列

在这里插入图片描述
在这里插入图片描述

结论

1.从5行6列的测试结果对比,改进后的程序极大的节省了时间
2.从6行6列的测试结果对比,改进后的程序极大的节省了内存空间

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值