`

Recursion: Solving a Maze

 
阅读更多

The Problem

A robot is asked to navigate a maze. It is placed at a certain position (the starting position) in the maze and is asked to try to reach another position (the goalposition). Positions in the maze will either be open or blocked with an obstacle. Positions are identified by (x,y) coordinates.

[Example of a simple maze]At any given moment, the robot can only move 1 step in one of 4 directions. Valid moves are:

 

  • Go North: (x,y) -> (x,y-1)
  • Go East: (x,y) -> (x+1,y)
  • Go South: (x,y) -> (x,y+1)
  • Go West: (x,y) -> (x-1,y)

Note that positions are specified in zero-based coordinates (i.e., 0...size-1, where size is the size of the maze in the corresponding dimension).

The robot can only move to positions without obstacles and must stay within the maze.

The robot should search for a path from the starting position to the goal position (a solution path) until it finds one or until it exhausts all possibilities. In addition, it should mark the path it finds (if any) in the maze. 

 


Representation

To make this problem more concrete, let's consider a maze represented by a matrix of characters. An example 6x6 input maze is:

 

S#####
.....#
#.####
#.####
...#.G
##...#
 
'.' - where the robot can move (open positions)
'#' - obstacles (blocked positions)
'S' - start position (here, x=0, y=0)
'G' - goal (here, x=5, y=4)

 


Aside: Remember that we are using x and y coordinates (that start at 0) for maze positions. A y coordinate therefore corresponds to a row in the matrix and anx coordinate corresponds to a column.


A path in the maze can be marked by the '+' symbol...

 

path refers to either a partial path, marked while the robot is still searching:
+#####
++++.#
#.####
#.####
...#.G
##...#
(i.e., one that may or may not lead to a solution). Or, a solution path:
S#####
++...#
#+####
#+####
.++#+G
##+++#
which leads from start to goal.

 

 


Algorithm

We'll solve the problem of finding and marking a solution path using recursion.

Remember that a recursive algorithm has at least 2 parts:

 

  • Base case(s) that determine when to stop.

     

  • Recursive part(s) that call the same algorithm (i.e., itself) to assist in solving the problem.

Recursive parts

Because our algorithm must be recursive, we need to view the problem in terms of similar subproblems. In this case, that means we need to "find a path" in terms of "finding paths."

Let's start by assuming there is already some algorithm that finds a path from some point in a maze to the goal, call it FIND-PATH(x, y).

Also suppose that we got from the start to position x=1, y=2 in the maze (by some method):

 

+#####
++...#
#+####
#.####
...#.G
##...#
What we now want to know is whether there is a path from x=1, y=2 to the goal. If there is a path to the goal from x=1, y=2, then there is a path from the start to the goal (since we already got to x=1, y=2).

 

To find a path from position x=1, y=2 to the goal, we can just ask FIND-PATH to try to find a path from the North, East, South, and West of x=1, y=2:

 

  • FIND-PATH(x=1, y=1) North
  • FIND-PATH(x=2, y=2) East
  • FIND-PATH(x=1, y=3) South
  • FIND-PATH(x=0, y=2) West

Generalizing this, we can call FIND-PATH recursively to move from any location in the maze to adjacent locations. In this way, we move through the maze.

Base cases

It's not enough to know how to use FIND-PATH recursively to advance through the maze. We also need to determine when FIND-PATH must stop.

One such base case is to stop when it reaches the goal.

The other base cases have to do with moving to invalid positions. For example, we have mentioned how to search North of the current position, but disregarded whether that North position is legal. In order words, we must ask:

 

  • Is the position in the maze (...or did we just go outside its bounds)?
  • Is the position open (...or is it blocked with an obstacle)?

Now, to our base cases and recursive parts, we must add some steps to mark positions we are trying, and to unmark positions that we tried, but from which we failed to reach the goal:

FIND-PATH(x, y)

  • if (x,y outside maze) return false
  • if (x,y is goal) return true
  • if (x,y not open) return false
  • mark x,y as part of solution path
  • if (FIND-PATH(North of x,y) == true) return true
  • if (FIND-PATH(East of x,y) == true) return true
  • if (FIND-PATH(South of x,y) == true) return true
  • if (FIND-PATH(West of x,y) == true) return true
  • unmark x,y as part of solution path
  • return false

All these steps together complete a basic algorithm that finds and marks a path to the goal (if any exists) and tells us whether a path was found or not (i.e., returns true or false). This is just one such algorithm--other variations are possible.

 


Note: FIND-PATH will be called at least once for each position in the maze that is tried as part of a path.

Also, after going to another position (e.g., North):

if (FIND-PATH(North of x,y)¹ == true) return true²

if a path to the goal was found, it is important that the algorithm stops. I.e., if going North of x,y finds a path (i.e., returns true¹), then from the current position (i.e., current call of FIND-PATH) there is no need to check East, South or West. Instead, FIND-PATH just need return true² to the previous call.

Path marking will be done with the '+' symbol and unmarking with the 'x' symbol.


Using Algorithm

To use FIND-PATH to find and mark a path from the start to the goal with our given representation of mazes, we just need to:

 

  1. Locate the start position (call it startxstarty).
  2. Call FIND-PATH(startx, starty).
  3. Re-mark* the start position with 'S'.

 


*In the algorithm, the start position (marked 'S') needs to be considered an open position and must be marked as part of the path for FIND-PATH to work correctly. That is why we re-mark it at the end.


Backtracking

An important capability that the recursive parts of the algorithm will give us is the ability to backtrack.

 

++####
#+#..#
#+#..#
#++#.#
###...
G...##
For example, suppose the algorithm just marked position x=2, y=3 in this maze. I.e, it is in the call to FIND-PATH(x=2, y=3). After marking...

 

 

++####
#+#..#
#+#..#
#++#.#
###...
G...##
First, it will try to find a path to the goal from the position North of x=2, y=3, calling FIND-PATH(x=2, y=2).

Since the North position is not open, the call FIND-PATH(x=2, y=2) will return false, and then it will go back (backtrack) to FIND-PATH(x=2, y=3) and resume at the step just after it went North.

 

 

++####
#+#..#
#+#..#
#++#.#
###...
G...##
Next, it will go East of x=2, y=3, calling FIND-PATH(x=3, y=3).

This position is not open, so it will backtrack to FIND-PATH(x=2, y=3) and resume at the step just after it went East.

 

 

++####
#+#..#
#+#..#
#++#.#
###...
G...##
Next, it will go South of x=2, y=3, calling call FIND-PATH(x=2, y=4).

This position is not open, so it will backtrack to FIND-PATH(x=2, y=3) and resume at the step just after it went South.

 

 

 

++####
#+#..#
#+#..#
#++#.#
###...
G...##
Finally, it will go West of x=2, y=3, calling FIND-PATH(x=1, y=3).

This position is not open, so it will backtrack to FIND-PATH(x=2, y=3) and resume at the step just after it went West.

Since West is the last direction to search from x=2, y=3, it will unmark x=2, y=3, and backtrack to the previous call, FIND-PATH(x=1, y=3).

 

Reference:

https://www.cs.bu.edu/teaching/alg/maze/

分享到:
评论

相关推荐

    recursion:人力资源项目

    在"recursion:人力资源项目"中,我们可以推测这个项目可能涉及到利用递归算法解决人力资源管理的一些问题,如数据结构的遍历、组织结构的层次表示或是员工关系的查找等。以下我们将深入探讨递归及其在JavaScript中的...

    recursion:我的递归项目

    在本项目“recursion:我的递归项目”中,我们将探讨如何在Python中实现递归,通过一些简单的示例来理解其工作原理和应用。 首先,我们需要理解递归的两个基本要素:基本情况(Base Case)和递归情况(Recursive ...

    recursion:递归练习

    这个名为"recursion:递归练习"的项目显然旨在深入理解和应用递归原理,特别是通过JavaScript语言来实现。JavaScript,作为一种广泛使用的脚本语言,不仅在网页开发中起到关键作用,还支持各种算法和数据结构的实现,...

    recursion:学习 repo - 递归练习

    在这个名为"recursion:学习 repo"的项目中,我们看到作者通过JavaScript语言实践了递归的使用。递归是函数或过程调用自身的一种技术,它在解决需要反复拆解问题的场景中尤为有效,比如遍历树形结构、计算阶乘或者...

    recursion-see-recursion:关于递归和垃圾的演讲幻灯片

    **标题解析:**"recursion-see-recursion:关于递归和垃圾的演讲幻灯片",这个标题表明了主题是关于编程中的递归概念,同时可能涉及到了内存管理,特别是“垃圾”的提及,可能指的是JavaScript环境中的垃圾回收机制。...

    Python_Recursion:使用Python的图形递归

    在提供的文件列表`Python_Recursion-main`中,我们可以预期找到一些关于使用Python递归处理图形问题的实际代码示例。这些示例可能包括树的遍历、图的深度优先搜索、分治算法(如快速排序或归并排序)、斐波那契数列...

    recursion:使用 Java 的递归函数示例

    递归 按定义:递归是递归过程或定义的重复应用。 要理解我介绍了计算器的答案,我们可以举一个例子递归,你想知道什么是资格成为美国的总理,这是假设: 您必须是美国公民,以下是规则: 您是美国(或)自然出生...

    recursion:递归算法

    递归简单(几乎)就是一切。 该项目包含递归算法,它展示了递归的简单性和强大的潜力,以及一个算法可视化工具。 这个可视化应用程序有一个算法接口,可以插入新算法,并提供一个接口来可视化和参数化算法。...

    recursion:基于递归的填充

    递归 这是我作为Hack Reactor的学生完成的一个项目。 这个项目是一对的。 该项目旨在探索递归的使用,同时重新实现以下本机javascript和DOM方法polyfill: getElementByClassName() parseJSON() ...

    ruby_vs_java_recursion:Ruby 与 Java

    ruby_vs_java_recursion Ruby vs Java:为什么世界会用 Java 更快地结束? 该 repo 显示了 Hanoi Towers 上的经典递归调用。 上有Java、JRuby、Ruby实现的比较结果

    Recursion:如何实现递归的例子

    在编程领域,递归是一种强大的概念,它涉及函数或过程调用自身来解决问题。递归在许多编程语言中都有应用,其中包括Java。本篇将详细解释递归的概念、工作原理以及如何在Java中实现递归。 一、递归定义与原理 ...

    scrimshaw-recursion:Postgres数据建模

    Sparkify歌曲播放数据库数据库和ETL管道,用于优化有关用户播放了哪些歌曲的查询。档案: 等将ETL文件转换为表格的主要ETL过程eda.ipynb 对结果表进行探索性数据分析,绘制出前10位用户乙炔不在etl管道或数据库中...

    recursion:我在 Hack Reactor 做的一个项目

    在这个名为"recursion"的Hack Reactor项目中,我们深入探讨了JavaScript中的递归算法及其应用。递归是一种函数或过程调用自身的技术,通常用于处理数据结构如树、图,以及执行分治策略等问题。 1. **递归的基本原理...

    recursion:在 JavaScript 中实践基本的递归解决方案,包括河内塔

    #递归这是递归的两个基本示例。 ##因子这在数学中完成了同样的问题: 阶乘(n) = 阶乘(n * 阶乘(n - 1)) 阶乘(1)= 1 factorial(4) = 24因为factorial(4) = 4 * 3 * 2 * 1 function factorial(n) {if (n == 1) {...

    Introduction-to-Cybersecurity-and-Recursion:第10周

    网络安全和递归介绍 第10周:网络安全与递归简介 一,文件清单 敏捷方法和递归-Microsoft Word... const A = [ [1、2、3], [4、5、6], [7,8,9], ] 然后要求您顺时针旋转矩阵以将新的数字矩阵输出为[[7,4,1],

    Recursion:我的第一个递归代码

    在编程领域,递归是一种强大的概念,它涉及函数或过程调用自身来解决问题。递归在许多编程语言中都有应用,其中包括Java。本篇将详细探讨递归的基础、工作原理以及如何在Java中实现递归。 一、递归定义与原理 ...

    Understanding-Recursion:递归奇迹的无耻冗长指南

    **理解递归:递归奇迹的无耻冗长指南** 在计算机科学中,递归是一种强大的编程技术,它涉及函数或过程调用自身来解决问题。递归的核心在于它能够将复杂的问题分解为相同但规模更小的子问题,直到达到一个基础情况,...

    b-spline recursion:一个计算b-spline基原子的递归函数,非常紧凑-matlab开发

    一个计算网格上 b 样条点的函数用法 y = spline_recursion (u,n) n 是样条的阶数u 是网格点例子: t=linspace(-2,10,10000); y1=spline_recursion (t,2); y2=spline_recursion (t,3); y3=spline_recursion (t,4); y4...

    left-recursion:在Haskell解析器中消除左递归的快速说明

    parens :: Parsec String () a -> Parsec String () a parens p = between (char '(') (char ')') p ``` 在这个示例中,我们使用`chainl1`函数来构建带有优先级的解析器,它能自动处理左结合的运算符,而无需显式...

Global site tag (gtag.js) - Google Analytics