[英]Make Java recursive maze solver more efficient
更新
我可以通過將線程大小增加到幾GB來使我的算法正常工作,並且在一兩秒鍾之內就能解決1803x1803迷宮問題。
---------------
我昨天開始用Java教自己的遞歸。 我創建了一種算法,可以為迷宮拍照並解決。 但是,當做大於200x200 px的迷宮時,我會得到堆棧溢出的答案,因為我認為此算法的堆棧變得太長。 如何改善此算法,以便可以輸入最大1000x1000的圖像?
另外,您能告訴我我目前使用哪種算法嗎? 我相信這是DFS,但我不確定。
請解釋為什么您的解決方案更有效,以及使用的想法。
這是解決問題的主要方法
public class BlackWhiteSolver {
static int[][] solutionSet = new int[203][203];
static int width, height;
static String originalImage;
static int correctX, correctY;
public static void convert() {
try {
BufferedImage original = ImageIO.read(new File(originalImage));
int red;
int threshold = 2;
width = original.getWidth();
height = original.getHeight();
for(int i=0; i<original.getWidth(); i++) {
for(int j=0; j<original.getHeight(); j++) {
red = new Color(original.getRGB(i, j)).getRed();
// 1 = white, 0 = black, 9 = tried, 5 = solved
if(red > threshold) { solutionSet[i][j] = 1; }
else { solutionSet[i][j] = 0; }
}
}
} catch (IOException e) {e.printStackTrace();}
}
public BlackWhiteSolver(int solvedX, int solvedY, String pic) {
correctX = solvedX;
correctY = solvedY;
originalImage = pic;
}
public boolean solve (int row, int column) {
boolean completed = false;
if (validPoint(row, column)) {
solutionSet[row][column] = 9;
if (row == correctX && column == correctY) {
completed = true;
} else {
completed = solve (row+1, column);
if (!completed) {
completed = solve (row, column+1);
}
if (!completed) {
completed = solve (row-1, column);
}
if (!completed) {
completed = solve (row, column-1);
}
}
if (completed) {
solutionSet[row][column] = 5;
}
}
return completed;
}
private boolean validPoint (int row, int column) {
boolean isValid = false;
if (row < height-1 && column < width-1 && row >= 1 && column >= 1 ) {
if (solutionSet[row][column] == 1) {
isValid = true;
}
}
return isValid;
}
public static void solvedFile() {
BufferedImage binarized = new BufferedImage(width, height,BufferedImage.TYPE_3BYTE_BGR);
int newPixel = 0;
int rgb = new Color(255, 0, 0).getRGB();
for(int i=0; i<width; i++){
for(int j=0; j<height; j++)
{
if (solutionSet[i][j] == 0) {
newPixel = 0;
newPixel = colorToRGB(1, newPixel, newPixel, newPixel);
} else if (solutionSet[i][j] == 1 || solutionSet[i][j] == 9) {
newPixel = 255;
newPixel = colorToRGB(1, newPixel, newPixel, newPixel);
} else if (solutionSet[i][j] == 5) {
newPixel = 16711680;
}
binarized.setRGB(i, j, newPixel);
}
}
try { ImageIO.write(binarized, "gif",new File("maze-complete") );} catch (IOException e) {e.printStackTrace();}
}
private static int colorToRGB(int alpha, int red, int green, int blue) {
int newPixel = 0;
newPixel += alpha;
newPixel = newPixel << 8;
newPixel += red; newPixel = newPixel << 8;
newPixel += green; newPixel = newPixel << 8;
newPixel += blue;
return newPixel;
}
}
這是跑迷宮的課
public class BlackWhiteInterface
{
public static void main (String[] args) {
BlackWhiteSolver puzzle = new BlackWhiteSolver(60, 202, "maze-4.gif");
System.out.println();
puzzle.convert();
if (puzzle.solve(0,34)) {
System.out.println("completed");
puzzle.solvedFile();
} else {
System.out.println("not possible");
}
}
}
生成帶有起點和終點的正確迷宮
public class MazeBuilder {
static String start = "left";
static String end = "down";
public static void main(String[] args)
{
try
{
BufferedImage original = ImageIO.read(new File("mazeInput1.gif"));
BufferedImage binarized = new BufferedImage(original.getWidth(), original.getHeight(),BufferedImage.TYPE_BYTE_BINARY);
int red;
int redRightPixel;
int redUpPixel;
int newPixel;
int threshold = 2;
for(int i=0; i<original.getWidth(); i++)
{
for(int j=0; j<original.getHeight(); j++)
{
red = new Color(original.getRGB(i, j)).getRed();
int alpha = new Color(original.getRGB(i, j)).getAlpha();
if(red > threshold) { newPixel = 255; }
else { newPixel = 0; }
if (i == 0 || j == 0 || i == original.getWidth()-1 || j == original.getHeight() - 1){
newPixel = 0;
if (end == "left") {
} else if (end == "right") {
} else if (end == "up") {
} else if (end == "down") {
}
/*if (i == 1 || j == 1 || i == original.getWidth()-2 || j == original.getHeight() - 2 && red > 2) {
System.out.println("Start Point: (" + i + ", " + j + ")");
}
if (i == 0 && j > 0 && j < original.getHeight()-1) {
redRightPixel = new Color(original.getRGB(i+1, j)).getRed();
if (i == 0 && redRightPixel > 2) {
System.out.println("Start Point: (" + i + ", " + j + ")");
newPixel = 255;
}
}*/
/*if (j == original.getHeight()-1 && i > 0 && i < original.getWidth()-1) {
redUpPixel = new Color(original.getRGB(i, j-1)).getRed();
if (redUpPixel > 2) {
System.out.println("End Point: (" + i + ", " + j + ")");
newPixel = 255;
}
}*/
}
if (start == "left") {
if (i == 1 && j != 0 && j != original.getHeight()-1 && red > 2) {
System.out.println("Start Point: (" + i + ", " + j + ")");
}
} else if (start == "right") {
if (i == original.getHeight()-2 && j != 0 && j != original.getHeight()-1 && red > threshold) {
System.out.println("Start Point: (" + i + ", " + j + ")");
}
} else if (start == "up") {
if (j == 1 && i != 0 && i != original.getWidth()-1 && red > threshold) {
System.out.println("Start Point: (" + i + ", " + j + ")");
}
} else if (start == "down") {
if (j == original.getHeight()-2 && i != 0 && i != original.getWidth()-1 && red > threshold) {
System.out.println("Start Point: (" + i + ", " + j + ")");
}
}
if (end == "left") {
if (i == 1 && j != 0 && j != original.getHeight()-1 && red > 2) {
System.out.println("End Point: (" + i + ", " + j + ")");
}
} else if (end == "right") {
if (i == original.getHeight()-2 && j != 0 && j != original.getHeight()-1 && red > threshold) {
System.out.println("End Point: (" + i + ", " + j + ")");
}
} else if (end == "up") {
if (j == 1 && i != 0 && i != original.getWidth()-1 && red > threshold) {
System.out.println("End Point: (" + i + ", " + j + ")");
}
} else if (end == "down") {
if (j == original.getHeight()-2 && i != 0 && i != original.getWidth()-1 && red > threshold) {
System.out.println("End Point: (" + i + ", " + j + ")");
}
}
newPixel = colorToRGB(alpha, newPixel, newPixel, newPixel);
binarized.setRGB(i, j, newPixel);
}
}
ImageIO.write(binarized, "gif",new File("maze-4") );
}
catch (IOException e)
{
e.printStackTrace();
}
}
private static int colorToRGB(int alpha, int red, int green, int blue) {
int newPixel = 0;
newPixel += alpha;
newPixel = newPixel << 8;
newPixel += red; newPixel = newPixel << 8;
newPixel += green; newPixel = newPixel << 8;
newPixel += blue;
return newPixel;
}
}
203 x 203迷宮的示例輸出
一種稍微有效的簡單方法是不使用遞歸將到目前為止已遵循的路徑存儲在堆棧中。 取而代之的是將您到目前為止遵循的路徑存儲在java.util.BitSet
(在其中將每個路徑像素存儲在BitSet
y*width + x
元素中),或者您可以簡單地使用圖片的紅色區域上色以存儲路徑。
這樣可以避免堆棧溢出。
基本算法是從起點開始,並沿四個主要方向之一進行,除非您已經訪問過該方向(嘗試過並找到一個死胡同,或者從該方向來到這里)。 當您朝某個方向行駛時,您在那做同樣的事情。 這是一個簡單的非遞歸循環。
當您走到盡頭時,可以通過檢查所有四個方向(從何處看)來弄清楚最初是如何到達那里的。 您將紅色從站立的位置上移開,然后沿原來的方向返回。 如果在任何方向上都沒有紅色路徑,那么您將再次處於起點,並且您已嘗試了一切,因此無法解決迷宮問題。
當您回溯時,您可以在路徑上的較舊的正方形上嘗試尚未嘗試的下一個方向,直到所有方向都死胡同為止。
如果您到達終點,那就完成了。
這是一些通常無法處理循環的偽代碼(路徑在“圓圈”中),效率極低(例如,它應使用BitSet
而不是boolean[][]
),並且可能存在一些錯誤,但它給出了大致的想法:
public class MazeSolver {
private static enum Direction { UP, RIGHT, DOWN, LEFT }
// Return array's element is true if that's part of the path
public static boolean[][] solve(final boolean[][] mazeWallHere,
int x, int y,
final int endX, final int endY) {
final int width = mazeWallHere.length;
final int height = mazeWallHere[0].length;
final boolean[][] path = new boolean[width][height];
Direction nextDirection = Direction.UP;
boolean backtrack = false;
while (true) {
// If this spot is a dead end in all new directions, head back
if (backtrack) {
backtrack = false;
// Unmark where we are
path[x][y] = false;
// Find where we came from and what direction we took to get here
// Then switch to the next direction
// If all directions have been tried, backtrack again
// If we can't backtrack, return null because there's no solution
// If we went up to get here, go back down and try going right.
if (y != 0 && path[x][y - 1]) {
y--;
nextDirection = Direction.RIGHT;
continue;
}
// If we went right to get here, go back left and try going down.
else if (x != 0 && path[x - 1][y]) {
x--;
nextDirection = Direction.DOWN;
continue;
}
// If we went down to get here, go back up and try going left.
else if (y < height && path[x][y + 1]) {
y++;
nextDirection = Direction.LEFT;
continue;
}
// If we went left to get here, go back right and backtrack again.
else if (x < width && path[x + 1][y]) {
x++;
backtrack = true;
continue;
}
// If we didn't come from anywhere, we're at the starting point
// All possible paths are dead ends
else return null;
}
// Mark where we are
path[x][y] = true;
// If we've solved it, return the solution
if (x == endX && y == endY) return path;
// Move unless we:
// * hit the edge of the maze
// * it's the direction we originally got here from
// * hit a wall
// If we can't go a certain direction, try the next direction
// If we're out of directions to try, backtrack
switch (nextDirection) {
case UP: if (y == height
|| path[x][y + 1]
|| mazeWallHere[x][y + 1]) {
nextDirection = Direction.RIGHT;
continue;
}
else y++;
break;
case RIGHT: if (x == width
|| path[x + 1][y]
|| mazeWallHere[x + 1][y]) {
nextDirection = Direction.DOWN;
continue;
}
else x++;
break;
case DOWN: if (y == 0
|| path[x][y - 1]
|| mazeWallHere[x][y - 1]) {
nextDirection = Direction.LEFT;
continue;
}
else y--;
break;
case LEFT: if (x == 0
|| path[x - 1][y]
|| mazeWallHere[x - 1][y]) {
backtrack = true;
continue;
}
else x--;
break;
}
}
}
}
如果要正確處理循環,請將path
設為int[][]
並存儲移動號而不是true,以便您知道哪個路徑更舊。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.