[英]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.