[英]2 dimensional maze solver recursive function
我正在嘗試將二維矩陣實現為迷宮。 有一個起點,一個終點(隨機選擇)。 而且,要使其變得簡單一點,就存在障礙和因素。 如果老鼠碰到障礙物,應該回溯並找到正確的路徑。 如果遇到代理,它將被銷毀。 這是一個示例4x4矩陣
1 7 1 1
2 1 1 0
1 0 1 0
1 1 1 9
關鍵:0是障礙,2是代理,7是起點,9是目標/終點。 1表示可以安全地移動到那里。
該矩陣的正確解決方案是:
0 1 1 0
0 0 1 0
0 0 1 0
0 0 1 1
但是老鼠並不聰明(至少對於該程序而言),所以我正在實現帶有隨機動作的蠻力算法。
我嘗試使用稱為mazeUtil()的遞歸函數來實現此目的。 下面是函數:maze [] []是大鼠經過的隨機初始矩陣。
solution [] []是將跟蹤移動的解決方案矩陣。
(x,y)是網格中的當前位置n是矩陣的大小(它是一個正方形矩陣)。
public static void mazeUtil(int maze[][], int solution[][], int x, int y, int n)
{
if(x == goal[0] && y == goal[1])
{
solution[x][y] = 1;
return;
}
int check = moveCheck(maze, x, y, n);
//moveCheck() return 0 for Obstacle, 1 for safe path, 2 for agent, 7 for starting point (also safe path), 9 for goal (safe path)
if (check == 2){
solution[x][y] = 1;
out.println("Oops! Ran into an agent!");
return;
}
else if(check == 0)
{
//What should I put here?
}
else if(check == 1 || check == 7 || check == 9)
{
solution[x][y] = 1;
Random newRandom = new Random();
int temp = newRandom.nextInt(3);
if(temp == 0){ //move up if possible? x--
if(x > 0)
mazeUtil(maze, solution, x-1, y, n);
else
mazeUtil(maze, solution, x+1, y, n);
}
else if (temp == 1){
if (x < n-1)
mazeUtil(maze, solution, x+1, y, n);
else
mazeUtil(maze, solution, x-1, y, n);
}
else if(temp == 2){
if (y < n-1)
mazeUtil(maze, solution, x, y+1, n);
else
mazeUtil(maze, solution, x,y-1, n);
}
else if (temp == 3){
if (y > 0)
mazeUtil(maze, solution, x, y-1, n);
else
mazeUtil(maze, solution, x, y+1, n);
}
}
}
我必須隨機移動,這就是為什么我使用隨機函數。 如果我的函數遇到代理(2),則效果很好。 我還阻止了老鼠越界。 通過安全路徑(1)毫無問題。 但是問題是當它遇到障礙時。 我正在考慮回溯。 如何將其添加到函數中? 喜歡保存最后一步,然后相反嗎? 像這樣的迷宮很可能沒有解決方案
7 0 0 9
2 0 1 1
0 1 0 0
1 2 0 1
如果執行正確,它將遇到障礙,如果發生故障,它將遇到代理。 它不能對角移動。 這使我想到第二個問題,在這種情況下我將如何終止遞歸函數。 此時,它唯一終止的時間是達到目標或擊中特工時。
任何幫助,將不勝感激。 提前致謝
好吧,讓我們想象一下,我需要以解決問題的方式來解決相同的問題。 (我認為最好的解決方案是找到路徑,正如評論中已經提到的那樣)。
我會創造
類Point {public int x; 公共領域 }
並在其中存儲坐標。
List<Point> path
在此解決方案中,您對上一點沒有任何問題(這是列表中的最后一點)
至於算法終止-您可以將算法與隨機數結合使用。 因此,您無法確定您的老鼠會解決最簡單的迷宮,例如
7 1 1
1 1 1
1 1 1
老鼠有可能永遠從(0,0)移至(1,0),從(1,0)移至(0,0)。
因此,讓我們再次想象一下,我需要改進您的算法,而不是使用好的算法。
我將在path
列表中存儲老鼠從障礙物返回或訪問該點的次數。 如果此number > 4
我將命令我的老鼠回到原始點(第7點)。 並重新開始旅程。
如果大鼠需要返回,例如10次,則算法終止。
同樣,您的算法很有趣,看到老鼠如何運動應該很有趣,但是它不能解決問題。 它不適用於大迷宮。
嘗試實施路徑查找。 如果您有問題-提出問題。
祝好運!
如果您想隨意移動,則需要知道您已經處於其中的狀態,因此您將需要一棵樹,否則當老鼠處於多向位置時,您可以保持最左邊的路徑。
現在讓我們考慮遞歸+隨機。 不可能那么難。 您可以使用一個函數來返回已存在的點的列表,並獲取正確的位置作為輸入,這有一些問題,白痴老鼠可以找回他已經來過的地方,所以讓我們通過添加來解決它前一點作為我們功能的另一個輸入。
每件事都到位。 現在我們想知道白痴老鼠是走在一條死路還是一個特工。 在這種情況下如何創建2個異常並在遞歸函數中處理它們呢?
好吧,我認為途中不會有更多問題。 實際上,我很想親自嘗試一下。 那會很有趣:D
白痴老鼠祝你好運
在提出解決方案之前,我想對您的算法設計進行一些分析。
您提到您要使用隨機游走算法。 毫無疑問,這是尋找路徑的一種完全可以接受(盡管不一定有效)的方式。 但是,您需要意識到它會帶來一些影響。
我實際上看不到代理和障礙之間的區別。 在這兩種情況下,您都需要回溯並找到其他路徑。 如果存在差異,則需要指出。
因此,假設您的迷宮有零條或更多條成功的路徑,並且您沒有在尋找最佳路徑(在這種情況下,您確實應該使用A *或類似的路徑),則解決方案的結構應類似於:
public List<Position> findPath(Set<Position> closedSet, Position from, Position to) {
if (from.equals(to))
return List.of(to);
while (from.hasNeighboursNotIn(closedSet)) {
Position pos = from.getRandomNeighbourNotIn(closedSet);
closedSet.add(pos);
List<Position> path = findPath(closedSet, pos, to);
if (!path.isEmpty())
return List.of(pos, path);
}
closedSet.add(from);
return Collection.EMPTY_LIST;
}
這使用了大量的偽代碼(例如,沒有List.of(item,list)),但是您知道了。
關於樣式的一個簡要說明,以后可以保存一些鍵入內容:maze [] [],solution [] []和n都是有效全局的,並且在遞歸調用之間不會更改(迷宮和解決方案只是作為對相同數組的引用傳遞的,並且n永遠不變)。 這純粹是樣式,但是您可以這樣寫:
private static int[][] maze;
private static int[][] solution;
private static int n;
public static void mazeUtil(int x, int y) {
...
}
接下來是您的解決方案:第一點是我看不出您何時達到目標。 您的mazeUtil函數不返回任何內容。 對於這種遞歸,一種通用方法是讓您的求解器函數返回一個布爾值:如果已達到目標,則返回true;否則返回false。 一旦得到一個真實的結果,就可以將其一直傳遞回調用堆棧。 每次您得到錯誤的答案,您就回溯到下一個解決方案。
所以我建議:
public static boolean mazeUtil(int x, int y) {
// return true if goal found, false otherwise
...
}
接下來,我不確定代理與障礙之間的實際區別是什么:碰到任何一個都會導致您回溯。 所以我認為那段代碼將是:
if (check == 2) {
out.println("Oops! Ran into an agent!");
return false;
}
if (check == 0)
out.println("Oops! Ran into an obstacle!");
return false;
}
然后是遞歸位:此處的一點是,您永遠不要將失敗的嘗試的解決方案重置為0(實際上,由於最終算法永遠不會回退超過一個步驟,這實際上並不那么重要,但是很好地說明一般方法)。 鑒於我們到目前為止所擁有的,現在應該是這樣的:
if (check == 9) {
out.println("Found the goal!");
return true;
}
if (check == 1 || check == 7) {
// add current position to solution
solution[x][y] = 1;
// generate random move within bounds
int nextX = ...
int nextY = ...
if (mazeUtil(nextX, nextY)) {
// we've found the solution, so just return up the call stack
return true;
}
// this attempt failed, so reset the solution array before returning
solution[x][y] = 0;
return false;
}
// shouldn't ever get here...
throw new IllegalStateException("moveCheck returned unexpected value: " + check);
是的,到目前為止還不錯,但是仍然存在問題。 一旦其中一個mazeUtil調用返回一個值(true或false),它將一直沿調用堆棧返回該值。 因此,如果您碰巧在特工或障礙物之前找到出口,那很好,但這是不太可能的。 因此,您無需嘗試遞歸時的任何舉動,而是需要嘗試所有可能的舉動。
與支持類Point一起,包含簡單的x和y對:
if (check == 1 || check == 7) {
// add current position to solution
solution[x][y] = 1;
// generate an array of all up/down/left/right points that are within bounds
// - for a random path need to randomise the order of the points
Point[] points = ...
for (Point next : points) {
if (mazeUtil(next.x, next.y)) {
// we've found the solution, so just return up the call stack
return true;
}
}
// this attempt failed, so reset the solution array before returning
solution[x][y] = 0;
return false;
}
我認為這與一只完全無知的老鼠差不多! 要查看其工作原理,請考慮以下迷宮:
7 1
0 9
從“ 7”開始,可能的移動是向下和向右。
從“ 1”開始,可能的移動是向下和向左:
這就是所有可能發生的事情。 因此,將*用作return-false-backtrack和! 為了取得成功,可能有以下任何一種情況:
R-D!
R-L-D*-R-D!
R-L-R-L-R-L-R-L (keep going for a long, long time....) R-L-R-D!
因此,對於可解決的迷宮和真正的隨機生成器,盡管可能需要很長時間,但最終將解決迷宮問題。 但是要注意的一點是,它實際上並沒有回溯那么多:僅從2或0節點開始只有一步。
但是,仍然存在無法解決的迷宮問題,對於完全無知的老鼠,我認為這是不可能的。 這樣做的原因是,對於像這樣的蠻力遞歸,只有兩種可能的終止條件:
對於一只完全無知的老鼠,無法檢測到第二只老鼠!
考慮以下迷宮:
7 1 1 1
0 0 0 0
0 0 0 0
1 1 1 9
完全無知的老鼠將永遠在第一行中左右徘徊,因此程序永遠不會終止!
解決方案是,老鼠必須至少具有一定的智能,並記住它的所在位置(這也將使可解決的迷宮在大多數情況下運行更快,並沿着整個路徑回溯而不是僅針對單個節點)。 但是,這個答案已經太久了,因此,如果您對此感興趣,我將在這里為您介紹其他迷宮解決方法: Java遞歸迷宮求解器問題
哦,關於Random的最后兩點:
希望對您有幫助!
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.