A*搜索算法的JAVA实现 解棋盘爵士游历问题 BFS

A knight moves on a chessboard two squares up, down, left, or right followed by one square in one of the two directions perpendicular to the first part of the move (i.e., the move is L-shaped). Suppose the knight is on an unbounded board at square (0,0) and we wish to move it to square (x,y) in the smallest number of moves. (For example, to move from (0,0) to (1,1) requires two moves. The knight can move to board locations with negative coordinates.)

1. Explain how to decide whether the required number of moves is even or odd without constructing a solution.
2. Design an admissible heuristic function for estimating the minimum number of moves required; it should be as accurate as you can make it. Prove rigorously that your heuristic is admissible.

3.  Implement A* and use it to solve the problem using your heuristic. Create two scatter plots showing (a) the number of nodes expanded as a function of solution length, and (b) computation time as a function of solution length for a set of randomly generated problem instances.

思路分析:这是一道AI作业题,主要考察A*算法的实现。下面给出的A*实现用到的启发函数是generated node到目的地的Manhattan 距离的三分之一,因为爵士走日字形,最多跳跃距离不会超过3格,这是一个可接受的启发函数。实现主要是BFS,需要借助队列实现。

实现Code

import java.util.Comparator;
import java.util.PriorityQueue;

/**
 * Find the minimum number of moves of knight from
 * (0,0) to (x,y)
 * @author Liu Yang & Wei Hong
 * @mail [email protected] & [email protected]
 */

public class ChessboardSearchAstar {

	static int[][] actionCosts = {{2, 1}, {2, -1}, {-2, 1}, {-2, -1},
						   {1, 2}, {1, -2}, {-1, 2}, {-1, -2}};

	private Node startNode;
	private Node goalNode;
	int MAX_X = 1999;
	int MAX_Y = 1999;
	int MIN_X = -2000;
	int MIN_Y = -2000;
	int visitedFlagOffset = 2000; // x + visitedFlagOffset = index of x in visitedFlag

	static int generatedNodeCount;
	static int expandedNodeCount;

	private enum flagType{
		INIT, VISITED;
	}

	private static flagType[][] visitedFlag;

	public int getVisitedFlagOffset() {
		return visitedFlagOffset;
	}

	public void setVisitedFlagOffset(int visitedFlagOffset) {
		this.visitedFlagOffset = visitedFlagOffset;
	}

	public Node getStartNode() {
		return startNode;
	}

	public void setStartNode(Node startNode) {
		this.startNode = startNode;
	}

	public Node getGoalNode() {
		return goalNode;
	}

	public void setGoalNode(Node goalNode) {
		this.goalNode = goalNode;
	}

	//return -1 when there is no solution or illegal input
	public int astarSearch(){
		if(startNode.getX() == goalNode.getX() && startNode.getY() == goalNode.getY()){
			return 0;
		}

		//since the startNode is fixed, only need to check goalNode
		if(!(goalNode.getX() >= MIN_X && goalNode.getX() <= MAX_X) || !(goalNode.getY() >= MIN_Y && goalNode.getY() <= MAX_Y)){
			System.err.println("illegal input!");
			return -1;
		}
		Comparator<Node> comparator = new Comparator<Node>() {

			@Override
			public int compare(Node n1, Node n2) {
				// TODO Auto-generated method stub
				if(n1.getFValue(goalNode) > n2.getFValue(goalNode)){
					return 1;
				} else if(n1.getFValue(goalNode) == n2.getFValue(goalNode)){
					return 0;
				}
				return -1;
			}
		};

		PriorityQueue<Node> pQueue = new PriorityQueue<Node>(2, comparator);
		//init visitedFlag
		visitedFlag = new flagType[MAX_X - MIN_X + 1][MAX_Y - MIN_Y + 1];
		for(int i = 0; i < visitedFlag.length; i++){
			for(int j = 0; j < visitedFlag[i].length; j++){
				visitedFlag[i][j] = flagType.INIT;
			}
		}
		generatedNodeCount = 1;
		expandedNodeCount = 0;
		Node currentNode = new Node(startNode);
		startNode.setG(0);
		startNode.setStepCount(0);
		startNode.setParentNode(null);
		pQueue.add(startNode);
		visitedFlag[startNode.getX() + visitedFlagOffset][startNode.getY() + visitedFlagOffset] = flagType.VISITED;

		while(pQueue.size() != 0 ){
			pQueue.poll();
			expandedNodeCount++;
			visitedFlag[currentNode.getX() + visitedFlagOffset][currentNode.getY() + visitedFlagOffset] = flagType.VISITED;
			for(int i = 0; i < actionCosts.length; i++){
				Node childNode = new Node(currentNode.getX() + actionCosts[i][0], currentNode.getY() + actionCosts[i][1]);
				if((childNode.getX() >= MIN_X) && (childNode.getX() <= MAX_X) && (childNode.getY() >= MIN_Y) && (childNode.getY() <= MAX_Y) &&
						visitedFlag[childNode.getX() + visitedFlagOffset][childNode.getY() + visitedFlagOffset] == flagType.INIT ){
					generatedNodeCount++;
					childNode.setParentNode(currentNode);
					childNode.setG(currentNode.getG() + 1);
					childNode.setStepCount(currentNode.getStepCount() + 1);
					pQueue.add(childNode);
					visitedFlag[childNode.getX() + visitedFlagOffset][childNode.getY() + visitedFlagOffset] = flagType.VISITED;
				}
			}
			currentNode = pQueue.peek();
			if(currentNode.getX() == goalNode.getX() && currentNode.getY() == goalNode.getY()){
				 return currentNode.stepCount;
			}
		}
		System.out.println("There is no solution for the input!");
		return -1;
	}

	/**
	 * @param for testing
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub

		ChessboardSearchAstar chessSearch =  new ChessboardSearchAstar();
		chessSearch.setStartNode(new Node(0,0));
		chessSearch.setGoalNode(new Node(1,1));
		//tracking the computation time
		long startTime = System.currentTimeMillis();
		System.out.println("the minimum number of moves of knight is " + chessSearch.astarSearch());
		long endTime = System.currentTimeMillis();
		System.out.println("The computation time is " + (endTime - startTime) + "ms");
		System.out.println("expandedNodeCount: " + expandedNodeCount + " " + "generatedNodeCount: " + generatedNodeCount);
	}

	static class Node {
		Node parentNode;
		int x, y; //coordinates
		int g; //current cost
		int stepCount;//count of step

		public Node(int i, int j) {
			// TODO Auto-generated constructor stub
			this.x = i;
			this.y = j;
		}

		public Node(Node startNode) {
			// TODO Auto-generated constructor stub
			this.x = startNode.x;
			this.y = startNode.y;
		}

		public Node() {
			// TODO Auto-generated constructor stub
		}

		public int getStepCount() {
			return stepCount;
		}

		public void setStepCount(int stepCount) {
			this.stepCount = stepCount;
		}

		public Node getParentNode() {
			return parentNode;
		}

		public void setParentNode(Node parentNode) {
			this.parentNode = parentNode;
		}

		public int getX() {
			return x;
		}

		public void setX(int x) {
			this.x = x;
		}

		public int getY() {
			return y;
		}

		public void setY(int y) {
			this.y = y;
		}

		public int getG() {
			return g;
		}

		public void setG(int g) {
			this.g = g;
		}

		public int getFValue(Node goalNode){
			return g + getHValue(goalNode);
		}

		public int getHValue(Node goalNode){
			return (Math.abs(x - goalNode.x) + Math.abs(y - goalNode.y)) / 3;
		}
	}
}
时间: 2024-10-09 14:43:19

A*搜索算法的JAVA实现 解棋盘爵士游历问题 BFS的相关文章

Dijkstra算法(三)之 Java详解

前面分别通过C和C++实现了迪杰斯特拉算法,本文介绍迪杰斯特拉算法的Java实现. 目录 1. 迪杰斯特拉算法介绍 2. 迪杰斯特拉算法图解 3. 迪杰斯特拉算法的代码说明 4. 迪杰斯特拉算法的源码 转载请注明出处:http://www.cnblogs.com/skywang12345/ 更多内容:数据结构与算法系列 目录 迪杰斯特拉算法介绍 迪杰斯特拉(Dijkstra)算法是典型最短路径算法,用于计算一个节点到其他节点的最短路径. 它的主要特点是以起始点为中心向外层层扩展(广度优先搜索思想

Kruskal算法(三)之 Java详解

前面分别通过C和C++实现了克鲁斯卡尔,本文介绍克鲁斯卡尔的Java实现. 目录 1. 最小生成树 2. 克鲁斯卡尔算法介绍 3. 克鲁斯卡尔算法图解 4. 克鲁斯卡尔算法分析 5. 克鲁斯卡尔算法的代码说明 6. 克鲁斯卡尔算法的源码 转载请注明出处:http://www.cnblogs.com/skywang12345/ 更多内容:数据结构与算法系列 目录 最小生成树 在含有n个顶点的连通图中选择n-1条边,构成一棵极小连通子图,并使该连通子图中n-1条边上权值之和达到最小,则称其为连通网的

哈夫曼树(三)之 Java详解

前面分别通过C和C++实现了哈夫曼树,本章给出哈夫曼树的java版本. 目录 1. 哈夫曼树的介绍 2. 哈夫曼树的图文解析 3. 哈夫曼树的基本操作 4. 哈夫曼树的完整源码 转载请注明出处:http://www.cnblogs.com/skywang12345/ 更多内容:数据结构与算法系列 目录 哈夫曼树的介绍 Huffman Tree,中文名是哈夫曼树或霍夫曼树,它是最优二叉树. 定义:给定n个权值作为n个叶子结点,构造一棵二叉树,若树的带权路径长度达到最小,则这棵树被称为哈夫曼树. 这

Java文件解压之TGZ解压

package com.alibaba.intl.batch.dependency; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import org.apache.commons.compress.archivers.tar.TarArchiveEntry

java代码解压zip文件

import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.Enumeration; import org.apache.tools.zip.ZipEntry; import org.apache.tools.zip.ZipFile; import org.springframework.stereoty

java压缩解压zip文件,中文乱码还需要ant.jar包

va] view plaincopyprint? package cn.cn; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.Enumeration; i

java加密解压类库

java压缩类库_支持加密解压.zip public static void unzip(File zipFile, String dest, String passwd) throws ZipException {          ZipFile zFile = new ZipFile(zipFile);  // 首先创建ZipFile指向磁盘上的.zip文件          zFile.setFileNameCharset("GBK");      // 设置文件名编码,在GB

JAVA zip解压 MALFORMED 错误

最近在在使用zip 解压时,使用JDK1.7及以上版本在解压时,某些文件会报异常 Exception in thread "main" java.lang.IllegalArgumentException: MALFORMED at java.util.zip.ZipCoder.toString(ZipCoder.java:58) at java.util.zip.ZipFile.getZipEntry(ZipFile.java:567) at java.util.zip.ZipFil

2017-9月微信公众号支付-Java详解

微信支付源代码 在此之前,先C麻瓜藤N遍,MD官方文档一半正确一半错误.言归正传, 微信支付整体流程:微信授权登录商户的公众号--微信支付的公众号配置--统一下单--微信js调起支付页面--输入密码支付--支付成功,异步回调URL处理商户的相应业务 一.业务场景: 先看一下支付的业务场景:用户使用微信登录商户页面,点击支付按钮,调起微信支付,选择付款卡号,输入密码,完成支付,如图: 场景十分简单,不过步骤比较多,稍不注意就掉坑里了. 二.微信公众号支付的配置准备: 1)调用公众号支付,首先你得有