【问题标题】:Solving maze with Backtracking使用回溯解决迷宫
【发布时间】:2015-08-31 08:44:34
【问题描述】:

我正在尝试使用回溯解决带有 scala 的迷宫。

我的问题是我不断收到 StackOverflow 错误。

我已经尝试了很多东西,但我总是以 StackOverflow 告终。

findStart() 和 getWay() 显示了我使用的两种方法。

我知道可以使用 Streams 执行此操作并在一次递归中计算所有可能的方法,但我想先通过回溯来解决它。

这是我的代码:

object MazeSolver {

  case class Cell(free: Boolean, start: Boolean)

  type Maze = Seq[Seq[Cell]]

  def main(args: Array[String]) = {

    val lab = lislab("laby1.rinth")

    val entrance = findStart(lab, Position(0, 0))


   println(getWay(lab, entrance(0), entrance(0), Nil))

  }

  def findStart(lab: Maze, pos: Position): List[Position] = {
    try {
      if (lab(pos.column)(pos.row).start)
        List(pos)
      else
        findStart(lab, pos.south) ::: findStart(lab, pos.north) ::: findStart(lab, pos.west) ::: findStart(lab, pos.east)
    } catch {
      case _: java.lang.IndexOutOfBoundsException => Nil
    }

  }

  def getWay(maze: Maze, pos: Position, lastPos: Position, way: List[Position]): List[Position] = {
    try {
      if (!maze(pos.column)(pos.row).free && !maze(pos.column)(pos.row).start) {
        Nil

      } else {

        if (isExit(pos, maze)) {
          pos :: way
        } else {

          val posList = List(pos.north, pos.south, pos.west, pos.east)

          posList.foreach { x => 
            val tail = getWay(maze, x, pos, way) 
            if(tail != Nil) tail :: way
            }


          Nil

        }
      }
    } catch { case _: java.lang.IndexOutOfBoundsException => way }

  }

  def lislab(fn: String): Maze = {
    try {
      (for (column <- io.Source.fromFile(fn, "UTF-8").getLines.toList)
        yield for (c <- column) yield Cell(c == ' ', c == '?')).toIndexedSeq
    } catch { case _: java.io.FileNotFoundException => Nil }
  }

  case class Position(column: Int, row: Int) {
    override def toString = "(" + column + "." + row + ")"
    def north = Position(column - 1, row)
    def south = Position(column + 1, row)
    def west = Position(column, row - 1)
    def east = Position(column, row + 1)
  }

  def isExit(pos: Position, lab: Maze): Boolean = {
    val cell = lab(pos.column)(pos.row)
    cell.free && (pos.column == 0
      || pos.column == lab.length - 1
      || pos.row == 0
      || pos.row == lab(0).length - 1)

  }
}

我做错了什么?如何使 findStart() 和 getWay() 函数正常工作?

【问题讨论】:

    标签: scala backtracking maze


    【解决方案1】:

    我现在只对findStart 发表评论。

    findStart 有两个问题:

    1. findStart 在每个相邻单元格上递归调用。不幸的是,任何邻居的相邻小区都是小区本身。
    2. 该函数从不检查您是否真的可以在给定的单元格上行走(我假设 Cell.free 意味着您可以在单元格上行走)。

    让我们看一个例子,看看为什么第一点会导致 StackOverflow。假设一个只有一行三列的迷宫:

    [A] <-> [B] <-> [C (start)]
    

    现在您的算法从A 开始。由于A.start == false,它调用findStart(A.east),即findStart(B)。现在因为B.start == false 它调用findStart(B.west)findStart(B.east)。第一个与findStart(A)相同,导致无限递归。

    要解决此问题,您需要跟踪已访问过的单元格。实现此目的的一种方法是传递已访问位置的列表。

    关于第 2 点,您只需检查某个位置是否可步行,通过检查maze(position.column)(position.row).free

    一个(未经测试的)实现可能如下所示:

    def findStart2(maze: Maze, position: Position, visited: List[Position]): List[Position] =
      if (maze(position.column)(position.row).start)
        List(position) // we reached our goal
      else {
        val explorationArea: List[Position] = List(position.north, position.east, position.south, position.west) filter (x => !visited.contains(x) && canWalkOnCell(maze, x))
        // filter the adjacent positions that have already been visited or are not walkable
        explorationArea flatMap { 
          notYetVisitedPosition =>
            findStart2(maze, notYetVisitedPosition, position :: visited) // don't forget to add the current position to the list of already visited positions
        }
      }
    
    private def canWalkOnCell(maze: Maze, position: Position): Boolean =
      indexInBound(maze, position) && maze(position.column)(position.row).free
    
    private def indexInBound(maze: Maze, position: Position): Boolean =
      position.column >= 0 && position.row >= 0 && position.column < maze.size && position.row < maze(position.column).size
    

    【讨论】:

    • findStart是寻找迷宫的入口。它将标有 ?。所以我猜不应该检查该字段是否免费。但如果我像这样过滤:filter (x =&gt; !visited.contains(x) &amp;&amp; indexInBound(maze, x)) 我仍然会陷入无限递归。
    • 我现在刚刚使用这种方法来寻找起点:def findStart2(maze: Maze, position: Position): Position = { for (a &lt;- 0 until maze.length) { for (b &lt;- 0 until maze(a).length) { if (maze(a)(b).start) return Position(a, b) } } Position(-1, -1) } 使用您对 getway() 函数的回答帮助我解决了这个问题。非常感谢
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-05-08
    • 1970-01-01
    • 2015-05-17
    • 1970-01-01
    • 1970-01-01
    • 2015-12-05
    • 1970-01-01
    相关资源
    最近更新 更多