简体   繁体   English

树状结构中的递归

[英]Recursion in a Tree-like Structure

As a school project I have to find the solution path in a maze using the backtracking method recursively, I usually have no problem solving algorithms with recursion, on linear problems, however when it comes of having multiple choices/paths to follow I don't know how to find only 1 solution.作为一个学校项目,我必须递归地使用回溯方法在迷宫中找到解决方案的路径,我通常没有用递归解决线性问题的算法问题,但是当涉及到有多个选择/路径要遵循时,我不知道如何只找到 1 个解决方案。

Problem parameters:问题参数:

  • A maze represented in a matrix which has multiple ways of getting to the same end point用矩阵表示的迷宫,它有多种到达同一终点的方式
  • The starting point起点

Language used:使用的语言:

  • F# F#

Output of the code:代码的输出:

████████████████████
█     █           ██
█ ███ ███ █████ █ ██
█   █   █   █   █ ██
█ █ █ █ █ █ █ ███ ██
█     █Sxxxx  █   ██
█ █████ █ █x███ █ ██
█     █   █xxx█   ██
█ █ █ ███████x██████
█ █ █   █   █x    ██
█ █ ███ █ █ █x███ ██
█   █   █ █  x█   ██
█ ███ ███ █ █x█ █ ██
█            x█   ██
███ █████████x███ ██
███ █     █xxx█ █ ██
███ █ █ █ █x███ █ ██
███   █ █xxx█     ██
█████████x██████████
█████████E██████████

#: Walls #: 墙壁
: Paths : 路径
E: End Point E:终点
S: Start Point S:起点

Portion of the code:部分代码:

let rec dfs(x,y,path,visited) =
        let rec checkVisited point visited = 
            match visited with
            | [] -> false
            | (x,y)::xs -> if point = (x,y) then true else checkVisited point xs
        let adjacents = [(x,y+1);(x+1,y);(x,y-1);(x-1,y)]
        for point in adjacents do
            if point = this.endPoint then
                this.solutionPath <- path
            else
                if checkVisited point visited = false && this.checkPoint point && this.isWall point = false then
                    dfs(fst(point),snd(point),(path@[point]),(visited@[(x,y)]))

This is another way (mooore optimized) of searching the solution in a maze这是在迷宫中搜索解决方案的另一种方式(摩尔优化)

let rec dfs(x,y,path) =
            // setting the point in the matrix visited (setting it to 'false')
            matrix.[y].[x] <- (fst(matrix.[y].[x]),false)
            // getting the adjacents of the point
            let adjacents = [(x,y+1);(x+1,y);(x,y-1);(x-1,y)]
            // iterate the adjacents
            for (x1,y1) in adjacents do
                // if the adjacent is the end point set the soultion path
                if (x1,y1) = this.endPoint then
                    this.solutionPath <- path
                else
                    // else check if the point is in the matrix and is not yet visited
                    if this.checkPoint(x1,y1) && snd(matrix.[y1].[x1]) <> false && this.isWall(x1,y1) = false then
                        // execute recursively the method in the point and add the current poisition to the path
                        dfs(x1,y1,((x1,y1)::path))
        dfs(x,y,[])

I have made it!我做到了! if you have any troubles doing this i will help you (even in other languages)!如果您在这样做时遇到任何麻烦,我会帮助您(即使是其他语言)!

Your current approach looks mostly okay.您目前的方法看起来基本没问题。 But because you're doing a depth first search the main issue you have is that there's nothing preventing you from getting stuck trying an infinitely long paths like [(1,1);(1,2);(1,1);...] instead of getting to more productive paths.但是因为您正在进行深度优先搜索,所以您遇到的主要问题是没有什么可以阻止您尝试像[(1,1);(1,2);(1,1);...]这样无限长的路径[(1,1);(1,2);(1,1);...]而不是走向更有成效的道路。 To avoid this you can either scan the path to see if the proposed next point is already in it (which takes time at most linear in the length of the list which may be fine for small problem sizes), or pass the set of visited points as an extra argument to the recursive function (which should allow faster membership queries).为避免这种情况,您可以扫描路径以查看建议的下一个点是否已经在其中(这最多需要与列表长度成线性关系的时间,这对于小问题规模可能很好),或者传递访问点的集合作为递归函数的额外参数(应该允许更快的成员资格查询)。

The other issue you have is that you don't have any way of combining the results of the different branches you could take.您遇到的另一个问题是您没有任何方法可以组合您可以采用的不同分支的结果。 One simple approach is to change the return type of your inner function to be an option type, and to return Some(path) from the top if and rewrite the else to something more like一种简单的方法是将内部函数的返回类型更改为选项类型,并从顶部返回Some(path) if并将 else 重写为更像

[x, y+1
 x, y-1
 x+1, y
 x-1, y]
|> List.tryPick (fun (x',y') -> if this.checkPoint(x',y') then 
                                    sol(x', y', (x,y)::path)
                                else None)

This is recursively attempting each possible direction in turn and returning whatever the first successful one is.这是递归地依次尝试每个可能的方向并返回第一个成功的方向。 This will not necessarily return the shortest path because it's a depth-first search.这不一定会返回最短路径,因为它是深度优先搜索。 You could also easily create a variant that returns a list of all possible paths instead of using an option (the biggest change would be using List.collect instead of List.tryPick ), in which case you could choose the shortest solution from the list if you wanted to, although this would do a lot of intermediate computation.您还可以轻松创建一个返回所有可能路径列表的变体,而不是使用选项(最大的变化是使用List.collect而不是List.tryPick ),在这种情况下,您可以从列表中选择最短的解决方案,如果你想要,虽然这会做很多中间计算。

A more involved change would be to switch to a breadth-first search instead of depth-first, which would let you return a shortest path very easily.一个更复杂的变化是切换到广度优先搜索而不是深度优先,这会让你很容易地返回最短路径。 Conceptually, here's how one approach to that would be to keep track of a shortest path to all "seen" points (starting with just [S,[]] , along with a set of points whose children have not yet been explored (again starting with just [S] ). Then, as long as there are points to explore, collect all of their distinct children; for each one that doesn't yet have a known path, add the path and put it in the next set of children to explore.从概念上讲,这里有一种方法是跟踪所有“可见”点的最短路径(仅从[S,[]] ,以及尚未探索其子项的一组点(再次开始)只用[S] )。然后,只要有要探索的点,就收集他们所有不同的孩子;对于每个还没有已知路径的孩子,添加路径并将其放入下一组孩子探索。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM