2011/05/19
Game Of Life 实现最优寻址之二多线程
解决方案路径?2个:
5026146555
5084164555
寻址用时:31ms
有时可能出现少一个的解的情况。这是因为多线程中最优解可能在后面出现,之前出的解比如长度有11位,然后长度12,然后长度10这样,12位向后的解都不要了。所以10位的也就没有被放入容器中。单线程中不会出现这样的结果。现在多线程,可用节点容器availableNodes被插入节点就不是按照由小到大的顺序进行了。可能是小距离的线程执行慢了,就后插入了。所以才会出现上述的情况。
为了选取最优解和保证内存中实例化对象不溢出,需要在一定的时候就return,结束循环。
ServerMain.java
/******************************************************************************* * @file MazeOfLife * @brief MazeOfLife class * @note Copyright (C) 2010 * @author XuYD * @date 2011/05/08 * @version 1.0 ******************************************************************************/ package intel.mtp.life; import java.util.ArrayList; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ServerMain { /** executor thread server instance */ private ExecutorService pool = null; /** 网格地图 */ private int[][] mazeGrid = null; /** 队列 */ private MazeQueue queue = null; /** 线程池大小 */ private int poolSize = 0; /** * main method * @param args */ public static void main(String[] args) { new ServerMain(); } /** * constructor */ public ServerMain() { try { initialize(); } catch (Exception e) { DebugLog.printResultln(e); } } /** * initialize */ public void initialize() { /* get the machine's core */ poolSize = Runtime.getRuntime().availableProcessors() * 2; /* Thread pool count */ pool = Executors.newFixedThreadPool(poolSize); /*-------假设从gridin.txt读取的值如下 Start--------*/ /* 地图行 */ int rows = 7; /* 地图列 */ int cols = 7; Point curPoint = null; Point tarPoint = null; // mazeGrid = // new int[][]{{0, 0, 0, 0, 0, 0, 0}, {0, 1, 1, 0, 0, 0, 0}, {0, 1, 1, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0}, // {0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0}}; // /* (3,1)(3,5)*/ // curPoint = new Point(2, 2); // tarPoint = new Point(5, 5); // // mazeGrid = // new int[][]{{0, 0, 0, 0, 0, 0, 0}, {0, 1, 1, 0, 0, 0, 0}, {1, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0}, // {0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0}}; // // /* (3,1)(3,5)*/ // curPoint = new Point(2, 1); // tarPoint = new Point(2, 2); // // mazeGrid = // new int[][]{{0, 0, 0, 1, 0, 0, 0}, {0, 0, 1, 0, 1, 0, 0}, {0, 0, 0, 1, 0, 0, 0}, {0, 1, 0, 0, 0, 0, 0}, // {0, 0, 0, 1, 0, 0, 0}, {0, 0, 1, 0, 1, 0, 0}, {0, 0, 0, 1, 0, 0, 0}}; // /* (3,1)(3,5) 1/5*/ // curPoint = new Point(3, 1); // tarPoint = new Point(3, 5); // // mazeGrid = // new int[][]{{0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 1, 1, 0, 0}, // {0, 0, 0, 0, 0, 0, 0}, {0, 1, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0}}; // /* (3,1)(3,5) 1/5*/ // curPoint = new Point(5, 1); // tarPoint = new Point(1, 5); // mazeGrid = new int[][]{{0, 0, 0, 0, 0, 0, 0}, {0, 1, 0, 0, 0, 0, 0}, {0, 0, 0, 1, 1, 0, 0}, {0, 0, 1, 0, 1, 0, 0}, {0, 0, 1, 1, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0}}; /* (1,1)(5,5) 3/5 */ /* 当前点 */ curPoint = new Point(1, 1); /* 当前点 */ tarPoint = new Point(5, 5); // // mazeGrid = // new int[][]{{0, 0, 0, 0, 0, 0, 0}, {0, 1, 0, 1, 0, 1, 0}, {0, 0, 1, 0, 1, 0, 0}, {0, 0, 0, 1, 0, 0, 0}, // {0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0}}; // /* (1,3)(5,3) 4/5 */ // curPoint = new Point(1, 3); // tarPoint = new Point(5, 3); // // mazeGrid = // new int[][]{{1, 1, 0, 0, 0, 0, 0}, {1, 1, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 1, 0, 0, 0}, // {0, 0, 0, 0, 1, 1, 0}, {0, 0, 0, 0, 1, 0, 1}, {0, 0, 0, 0, 0, 1, 0}}; // /* (1,1)(5,5) 5/5 */ // curPoint = new Point(1, 1); // tarPoint = new Point(5, 5); /*-------假设从gridin.txt读取的值 END--------*/ queue = MazeQueue.getInstance(); if (curPoint == null || tarPoint == null || mazeGrid == null) { DebugLog.printResultln("没有可用的输入参数!"); } else { long start = System.currentTimeMillis(); searchStart(rows, cols, curPoint, tarPoint); long end = System.currentTimeMillis(); DebugLog.printResultln("寻址用时:" + (end - start) + "ms"); } System.exit(-1); } /** * search start * */ public void searchStart(int rows, int cols, Point curP, Point tarP) { CountDownLatch doneSignal = new CountDownLatch(poolSize); /* * 1.节点对象,当前点的上一个节点为null * 2.由于网格地图是变化的,不像迷宫,所以网格地图也要保存 */ Node curNode = new Node(curP, null, mazeGrid); /* 向队列末尾追加 */ queue.addAvailableNode(curNode); /* * 1.执行线程 * 2.不要使用for(int i=0; i< doneSignal.getCount();i++) * 3.因为CountDownLatch的值会变的,可能Loop到10次,值变为10了。 */ for (int i = 0; i < poolSize; i++) { pool.execute(new LifeThread(doneSignal, rows, cols, tarP)); } try { doneSignal.await(); } catch (InterruptedException e) { e.printStackTrace(); } pool.shutdown(); /* * 1.结果队列中的节点可能会出现少量的非最优解 * 2.是因为第一次找到结果节点的不是最优解,但是放入最先。 * 3.此处再次通过主线程过滤 */ int resultSize = queue.resultNodeSize(); /* 如果节点为0 */ if (resultSize == 0) { DebugLog.printResultln("没有可行的解决方案路径~!~"); } /* 如果节点为1 */ else if(resultSize == 1) { DebugLog.printResultln("解决方案路径为1个:"); printSearchResult(queue.getResultNodeFirst()); } /* 如果节点多于1个 */ else { ArrayList
* 循环从该节点获取前节点
* 前节点再向前索取,这样循环获取节点
*/ while ((curNode = node.preNode) != null) { /* 当前节点指向该节点 */ node = curNode; /* 压栈 */ stack.push(curNode); } /* 循环获取路径 */ String resultValue = ""; for (int k = stack.size() - 2; k >= 0; k--) { /*--------get the path result---------*/ resultValue += coordinatorToOrientation(stack.get(k + 1).point, stack.get(k).point); } DebugLog.printResultln(resultValue); } /** * 根据坐标求出方向0-8
* 1 1 1
* 1 0 1
* 1 1 1
* * @param prePoint 原始坐标 * @param nextPoint 目标坐标 * @param mazeGridCopy 网格数组副本 * * @return none */ private int coordinatorToOrientation(Point prePoint, Point nextPoint) { int orientation = -1; int distanceRow = nextPoint.x - prePoint.x; int distanceCol = nextPoint.y - prePoint.y; /* 水平向左:1,8,7 */ if (distanceCol == -1) { /* 向左上:1 */ if (distanceRow == -1) { orientation = 1; } /* 向左:8 */ else if (distanceRow == 0) { orientation = 8; } /* 向左下:7 */ else if (distanceRow == 1) { orientation = 7; } } /* 水平居中:2,0,6 */ else if (distanceCol == 0) { /* 水平向上:2 */ if (distanceRow == -1) { orientation = 2; } /* 水平:0 */ else if (distanceRow == 0) { orientation = 0; } /* 水平向下:6 */ else if (distanceRow == 1) { orientation = 6; } } /* 水平向右:3,4,5 */ else if (distanceCol == 1) { /* 水平向上:3 */ if (distanceRow == -1) { orientation = 3; } /* 水平:4 */ else if (distanceRow == 0) { orientation = 4; } /* 水平向下:5 */ else if (distanceRow == 1) { orientation = 5; } } return orientation; } }
LifeThread.java
/******************************************************************************* * @file LifeThread * @brief LifeThread class * @note Copyright (C) 2010 * @author XuYD * @date 2011/05/08 * @version 1.0 ******************************************************************************/ package intel.mtp.life; import java.util.concurrent.CountDownLatch; public class LifeThread implements Runnable { /** 线程计数器 */ private CountDownLatch doneSignal = null; /** 相邻节点计算结果网格地图 */ private int[][] ai = null; /** 目标点 */ private Point tarPoint = null; /** 地图行 */ private int rows = 0; /** 地图列 */ private int cols = 0; /** * 对象0-8个方向坐标
* 1 2 3
* 8 0 4
* 7 6 5
*/ private int[] x = {-1, -1, -1, 0, 1, 1, 1, 0, 0}; private int[] y = {-1, 0, 1, 1, 1, 0, -1, -1, 0}; /** 节点队列 */ private MazeQueue queue = null; /** while循环次数 */ private int count_T = 0; /** * constructor */ public LifeThread(CountDownLatch doneSignal, int rows, int cols, Point point) { this.rows = rows; this.cols = cols; this.tarPoint = point; this.doneSignal = doneSignal; ai = new int[rows + 2][cols + 2]; queue = MazeQueue.getInstance(); } /** * 开始寻找路径
* 容器的选择:数组->Queue->LinkedList->ConcurrentLinkedQueue(支持并发) * 1.bug1=>Queue的数组队列使用出界。解决:选到最优解就退出
* 2.bug2=>就是出现【584334047】和【5843340470】这样结果。解决:目标点检测的时候:是->print,break;不是->入队列。
* 3.bug3=>暂时没有出现,就是死循环,比如四个正方形点的时候。解决:如果在向0方向移动且节点一样就不入队列。
* 4.bug4=>那有没有这种情况,在某个循环范围内出现重复,比如走3-5-7-1,这样3和1节点是一样的情况。解决:需要加个判断。
* 6.期望=>无限地循环下去了,节点经过多次也能到达的取消,即选择最优解。
* 对于6=>当下一次找到刚好大于前一次节点的距离时候,就终止,说明之前的就是最优解,多次测试发现在MutilT中不适合。
*/ public void doSearch() { /* 当前点...true,移动点...false */ boolean isSelfNode = false; /* curNode */ Node curNode = null; /* 开始走,出栈,从head */ while ((curNode = queue.popAvailableNode()) != null) { /* TODO 节点的距离与最短比较是否已经超过 */ /* * i=0 => 矩阵(左上) * i=1 => 矩阵(向上) * i=2 => 矩阵(右上) * i=3 => 矩阵(向右) * i=4 => 矩阵(右下) * i=5 => 矩阵(向下) * i=6 => 矩阵(左下) * i=7 => 矩阵(向左) * i=8 => 矩阵(原处) */ for (int i = 0; i < x.length; ++i) { Node nextNode = new Node(new Point(), null, null); /* set the next node */ nextNode.preNode = curNode; nextNode.point.x = curNode.point.x + x[i]; nextNode.point.y = curNode.point.y + y[i]; nextNode.length = nextNode.preNode.length + 1; nextNode.setGridCopy(curNode.getGridCopy()); /* 判断是否向0方向移动 */ isSelfNode = nextNode.point.equals(curNode.point); /* 检查下一个点 */ if (!unitCheck(nextNode, isSelfNode)) { continue; } else { /* 尝试move到新节点 */ moveToNextPoint(nextNode); /* 检查是否遵循规则,按照规则修改网格地图的所以点 */ if (ruleScanCheck(nextNode)) { /* 1.如果等于目标点则存储起来 */ if (nextNode.point.equals(tarPoint)) { DebugLog.printResultln(Thread.currentThread().getName() + "========== " + printSearchResult(nextNode)); /* * 存储之前先判断是否是最优解 * 此处还是可能出现取到的值是旧的值(不是min) * 可能出现其他线程修改了这个值的情况。 */ if(!queue.addResultNode(nextNode)) { // DebugLog.printResultln(Thread.currentThread().getName() + "========== " + queue.resultNodeSize()); return; } /* 找到的话就不用再去循环试探了,循环结束开始重新弹出一个节点 */ break; } /* 2.如果不是目标点 */ else { /* 如果是向0方向移动且网格数组一样就不入队列,否则入队列 */ if ((!isSelfNode) || !(nextNode.compare(nextNode.preNode))) { queue.addAvailableNode(nextNode); } else { continue; } } } } } } } /** * 节点基本检查
* 1.处在地图范围内
* 2.节点是处于活动(0)状态
* 3.如果在当前不移动,则不用check2
* @param point * @param isSelfNode * @return boolean,OK...NG */ private boolean unitCheck(Node node, boolean isSelfNode) { int row = node.point.x; int col = node.point.y; /* 处在地图范围内 */ if ((row < 0 || row >= node.getGridCopy().length) || (col < 0 || col >= node.getGridCopy()[0].length)) { return false; } /* 点上没有节点状态 */ if (!isSelfNode && node.getGridCopy()[row][col] != 0) { return false; } return true; } /** * 从前节点移动到下一个节点 * @param node */ private void moveToNextPoint(Node node) { node.getGridCopy()[node.preNode.point.x][node.preNode.point.y] = 0; node.getGridCopy()[node.point.x][node.point.y] = 1; } /** * 节点规则检查
* 1.是否遵循规则,保证节点可以存活
* 2.存活的话,按照规则修改网格地图的节点
* * @param point * @return boolean,OK...NG */ private boolean ruleScanCheck(Node node) { /* 相邻节点计算结果网格地图初始化 */ for (int i = 0; i < ai.length; i++) { for (int j = 0; j < ai[0].length; j++) { ai[i][j] = 0; } } /* * 如果life数组中life[i][j] = 1则,设置al数组[i][j]四周都是+1
* 1 1 1
* 1 0 1
* 1 1 1
* 叠加之后,就可以算出life[i][j]点周围有几个节点,就是al[i][j]的值。 */ for (int i = 1; i < rows + 1; i++) { for (int j = 1; j < cols + 1; j++) { if ((node.getGridCopy()[i - 1][j - 1] & 1) == 1) { ai[i - 1][j - 1]++; ai[i - 1][j]++; ai[i - 1][j + 1]++; ai[i][j - 1]++; ai[i][j + 1]++; ai[i + 1][j - 1]++; ai[i + 1][j]++; ai[i + 1][j + 1]++; } } } int row = node.point.x; int col = node.point.y; /* 坐标点转换为数组(对角变换)获取节点周围节点个数 */ if ((ai[row + 1][col + 1] < 2) || (ai[row + 1][col + 1] > 3)) { return false; } // printArrayTest("--------节点走后地图更新前---------", node.getGridCopy()); // printArrayTest("--------节点走后地图重新计算---------", ai); /* * 数组按照规则重新布置处理
* 因为节点是同时发生的,所以保证有一个副本(ai数组)存在才能判断(周围节点个数),
* 否则life数据变更,而无法判断节点出生
*/ for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { /* 1.如果有节点存在 */ if (node.getGridCopy()[i][j] == 1) { /* 1.1 判断时候可以存活(周围为0或者1,或者大于3) */ if (ai[i + 1][j + 1] < 2 || ai[i + 1][j + 1] > 3) { node.getGridCopy()[i][j] = 0; } } /* 2.如果没有节点存在,如果周围节点数位3,则出生 */ else if (ai[i + 1][j + 1] == 3) { node.getGridCopy()[i][j] = 1; } } } // printArrayTest("--------节点走后地图更新后---------", node.getGridCopy()); return true; } /** * 输出寻址的结果,寻址到最短路径就结束 * * @param node * @return true...寻址结束;false...继续寻址 */ public String printSearchResult(Node node) { Node curNode = null; /* 创建堆栈,存放该节点 */ java.util.Stack
* 循环从该节点获取前节点
* 前节点再向前索取,这样循环获取节点
*/ while ((curNode = node.preNode) != null) { /* 当前节点指向该节点 */ node = curNode; /* 压栈 */ stack.push(curNode); } /* 循环获取路径 */ String resultValue = ""; for (int k = stack.size() - 2; k >= 0; k--) { /*--------get the path result---------*/ resultValue += coordinatorToOrientation(stack.get(k + 1).point, stack.get(k).point); } return resultValue; } /** * 根据坐标求出方向0-8
* 1 1 1
* 1 0 1
* 1 1 1
* * @param prePoint 原始坐标 * @param nextPoint 目标坐标 * @param mazeGridCopy 网格数组副本 * * @return none */ private int coordinatorToOrientation(Point prePoint, Point nextPoint) { int orientation = -1; int distanceRow = nextPoint.x - prePoint.x; int distanceCol = nextPoint.y - prePoint.y; /* 水平向左:1,8,7 */ if (distanceCol == -1) { /* 向左上:1 */ if (distanceRow == -1) { orientation = 1; } /* 向左:8 */ else if (distanceRow == 0) { orientation = 8; } /* 向左下:7 */ else if (distanceRow == 1) { orientation = 7; } } /* 水平居中:2,0,6 */ else if (distanceCol == 0) { /* 水平向上:2 */ if (distanceRow == -1) { orientation = 2; } /* 水平:0 */ else if (distanceRow == 0) { orientation = 0; } /* 水平向下:6 */ else if (distanceRow == 1) { orientation = 6; } } /* 水平向右:3,4,5 */ else if (distanceCol == 1) { /* 水平向上:3 */ if (distanceRow == -1) { orientation = 3; } /* 水平:4 */ else if (distanceRow == 0) { orientation = 4; } /* 水平向下:5 */ else if (distanceRow == 1) { orientation = 5; } } return orientation; } /** * TEST, array out * @param note * @param al */ public void printArrayTest(String note, int[][] arrPrint) { DebugLog.printInfoln(note); for (int j1 = 0; j1 < arrPrint.length; j1++) { DebugLog.printInfo("[" + j1 + "] "); for (int j = 0; j < arrPrint[0].length; j++) { DebugLog.printInfo(arrPrint[j1][j] + " "); if (j == arrPrint[j1].length - 1) { DebugLog.printInfoln(""); } } } } public void run() { doSearch(); // DebugLog.printResultln(Thread.currentThread().getName() + "========== End"); doneSignal.countDown(); } }
MazeQueue.java
/******************************************************************************* * @file Queue * @brief Queue class * @note Copyright (C) 2010 * @author XuYD * @date 2011/05/08 * @version 1.0 ******************************************************************************/ package intel.mtp.life; import java.util.ArrayList; import java.util.concurrent.ConcurrentLinkedQueue; public class MazeQueue { private static MazeQueue self = null; /** 是一种先进先出的队列结构,从底部进从头部出,存储可用的节点 */ private ConcurrentLinkedQueue
Node.java
/******************************************************************************* * @file Node * @brief Node class * @note Copyright (C) 2010 * @author XuYD * @date 2011/05/08 * @version 1.0 ******************************************************************************/ package intel.mtp.life; public class Node { /** 当前网格坐标点 */ Point point = null; /** 前一网格节点 */ Node preNode = null; /** 当前节点对应的网格地图 */ private int[][] gridCopy = null; /** 当前节点相对于起始点距离 */ int length = 0; /** * constructor */ public Node() { this(null, null, null); } /** * constructor * @param point * @param node * @param grid */ public Node(Point point, Node preNode, int[][] grid) { this.point = point; this.preNode = preNode; setGridCopy(grid); } /** * compare node differ * @param node * @return */ public boolean compare(Node node){ if(!point.equals(node.point)){ return false; } for (int i = 0; i < gridCopy.length; i++) { for (int j = 0; j < gridCopy[0].length; j++) { if(gridCopy[i][j] !=node.getGridCopy()[i][j]){ return false; } } } return true; } public Point getPoint() { return point; } public void setPoint(Point point) { this.point = point; } public Node getPrevious() { return preNode; } public void setPrevious(Node preNode) { this.preNode = preNode; } public int[][] getGridCopy() { return gridCopy; } public void setGridCopy(int[][] grid) { /* copy grid array */ if (grid != null && grid.length != 0) { gridCopy = new int[grid.length][grid[0].length]; for (int i = 0; i < grid.length; i++) { for (int j = 0; j < grid[0].length; j++) { gridCopy[i][j] = grid[i][j]; } } } } }
Point.java
/******************************************************************************* * @file Point * @brief Point class * @note Copyright (C) 2010 * @author XuYD * @date 2011/05/08 * @version 1.0 ******************************************************************************/ package intel.mtp.life; public class Point { int x = 0; int y = 0; public Point() { this(0, 0); } public Point(int x, int y) { this.x = x; this.y = y; } public boolean equals(Point p) { return (x == p.x) && (y == p.y); } @Override public String toString() { return "(" + x + "," + y + ")"; } }
DebugLog.java
/******************************************************************************/ /** @file DebugLog.java @brief DebugLog class @note Copyright (C) 2010 @author ThinkinGall @date 2010/08/30 @version 1.0 *******************************************************************************/ package intel.mtp.life; /** * debug output class * * @class DebugLog */ public class DebugLog { /** log printFatal */ private static Boolean suppressFatal = false; /** log printInfo */ private static Boolean suppressInfo = false; /** log result */ private static Boolean suppressResult = false; /** * Constructor * * @param none * @return none */ public DebugLog() { } /** * print info log content * * @param msg * Log contents * @return none */ public static void printResultln(Object msg) { if (suppressResult == true) { return; } System.out.println(msg); } /** * print info log content * * @param msg * Log contents * @return none */ public static void printResult(Object msg) { if (suppressResult == true) { return; } System.out.print(msg); } /** * print info log content * * @param msg * Log contents * @return none */ public static void printInfoln(Object msg) { if (suppressInfo == true) { return; } System.out.println(msg); } /** * print info log content * * @param msg * Log contents * @return none */ public static void printInfo(Object msg) { if (suppressInfo == true) { return; } System.out.print(msg); } /** * print fatal log content * * @param msg * Log contents * @return none */ public static void printFatalln(Object msg) { if (suppressFatal == true) { return; } System.out.println(msg); } /** * set debug log print state * * @param blInfo * @return none */ public static void setSuppressInfo(Boolean blInfo) { suppressInfo = blInfo; } }