Algorithm 解决迷宫的最简单方法是不可变

Algorithm 解决迷宫的最简单方法是不可变,algorithm,functional-programming,maze,Algorithm,Functional Programming,Maze,在学习了一些Scala和FP的好处之后,我正在重新完成以前的一些CS作业,以便更好地理解FP。然而,我得到了一个任务,用FP实现它似乎是不切实际的(或者至少是简单的翻译) 当解一个简单的2D迷宫时,有必要记住哪些节点被访问过。但是,如果没有共享状态,每个递归调用如何知道其他递归调用检查了哪些节点?我可以将迷宫作为参数传递给每个递归调用,并返回一个新的迷宫,其中包含访问的位置,但这似乎过于计算密集,无法在每个递归调用中复制整个迷宫。是否需要更高级的方法来实现不可变迷宫解算器?您可以传递一组包含访问

在学习了一些Scala和FP的好处之后,我正在重新完成以前的一些CS作业,以便更好地理解FP。然而,我得到了一个任务,用FP实现它似乎是不切实际的(或者至少是简单的翻译)


当解一个简单的2D迷宫时,有必要记住哪些节点被访问过。但是,如果没有共享状态,每个递归调用如何知道其他递归调用检查了哪些节点?我可以将迷宫作为参数传递给每个递归调用,并返回一个新的迷宫,其中包含访问的位置,但这似乎过于计算密集,无法在每个递归调用中复制整个迷宫。是否需要更高级的方法来实现不可变迷宫解算器?

您可以传递一组包含访问节点(或其ID/名称,如果节点本身在设置中具有同等性)。向不可变集合中添加项通常需要
O(logn)
,检查集合中是否包含元素也是如此。因此,这比复制迷宫要便宜得多。

您可以传递一组包含已访问节点(或其ID/名称,如果节点本身在设置中具有同等性)。向不可变集合中添加项通常需要
O(logn)
,检查集合中是否包含元素也是如此。所以这比复制迷宫要便宜得多。

也许你注意到我先前的答案被删除了。虽然我是在开玩笑,只是建议“计算机以红色显示所有死角,以绿色显示连接入口和出口的路径”,同时,这是我对功能范式理解的隐喻——一种包含预计算确定性的隐喻。鉴于我有限的理解和知识,我在Haskell中做了一个例子,避免了递归深度搜索,计算4x5迷宫的路径,给定了一个数组,其中迷宫中的每个单元(即每个数组元素)只包含它可以连接到的单元的索引;入口-1,出口-2。(你可以在代码部分的顶部看到迷宫的轮廓。)我知道,经验丰富的程序员可以做得更多更好。请让我知道这是否符合这个问题的精神(安德鲁,谢谢你提出的有趣的挑战/指导)

{-maze-}
[E] =[]=[]=[]
|
[ ]=[ ]=[ ]=[ ]
|       |
[ ] [ ]=[ ] [ ]
|   |       |
[ ] [ ]=[ ]=[ ]
|   |
[]=[]=[]=[E]
导入数据。列表
导入数据,也许吧
--迷宫中的每个元素都列出了连接单元的索引,'-1'表示入口,'-2'表示出口
迷宫=[-1,1],[0,2,5],[1,3],[2],
[5],     [4,6,1,9],   [5,7],   [6,11],
[12],    [5,13,10],   [9],     [7,15],
[8,16],  [14,9,17],   [13,15], [14,11],
[12,17], [13,16,18],  [17,19], [18,-2]]
迷宫'=[-1,1],[0,2],[1,3],[2,7],
[8,5],   [4,6],    [5,7],   [3,6],
[4,9],   [8,10],   [9,11],  [10,15],
[16,13], [12,14],  [13,15], [11,14],
[12,17], [16,18],  [17,19], [18,-2]]
索引a=fromJust$elemIndex a迷宫
索引a=映射(索引)a
a连接索引a索引b=元素索引a(迷宫!!索引b)
isStart a--(a::单元)
|元素(-1)a=真
|否则=假
I发送一个--(a::单元格)
|元素(-2)a=真
|否则=假
hasStart a--(a::[cell])
|isStart(头部a)=正确
|否则=假
hasEnd a--(a::[cell])
|isEnd(最后一个a)=真
|否则=假
isSequenced(w:x:xs)(y:z:zs)——包括重叠的可能性,因为我们不知道溶液中有多少个单元
|areConnected(索引$last xs)(索引y)
||最后xs==y
||设(b:c:cs)=[c,b]==y,z]=True中的反向(w:x:xs)
|否则=假
移除回溯(x:xs)
|(x:xs)=[]
|xs==[]=[x]
|x==磁头xs=移除回程xs
|长度xs>1&&x==let(y:ys)=xs在头部ys=移除回程(尾部xs)
|否则=x:removeBacktracks xs
--列出死胡同
死角=过滤器(\x->length x==1&&find(=-1))x==Nothing)迷宫
死胡同索引=映射(索引)死胡同
已连接的代码(x:xs)
|x`elem`dead\u ends\u index=True
|not(x`elem`dead_ends_index)&&xs==[]=False
|否则=connectedToDeadEnd xs
--从死胡同中首先列出
第一个\u从\u死\u结束=过滤器(\x->length x==2&&find(=-1))x==Nothing&&connectedtodedadend x)迷宫
--创建序列

过滤=[l | l也许你注意到我先前的答案被删除了。虽然我是开玩笑,只是建议“计算机以红色显示所有死角,以绿色显示连接入口和出口的路径,”同时,这也是我对功能范式理解的一种隐喻——一种包含预计算的确定性。鉴于我有限的理解和知识,我在Haskell做了一个例子,避免了递归深度搜索,计算4x5迷宫的路径,给出了迷宫中每个单元所在的数组(即,每个数组元素)只包含它可以连接到的单元格的索引;-1表示入口,-2表示出口。(你可以在代码部分的顶部看到迷宫的轮廓。)我知道,更有经验的程序员可以做得更多更好。请告诉我这是否符合这个问题的精神(安德鲁,谢谢你的有趣挑战/指导)

{-maze-}
[E] =[]=[]=[]
|
[ ]=[ ]=[ ]=[ ]
|       |
[ ] [ ]=[ ] [ ]
|   |       |
[ ] [ ]=[ ]=[ ]
|   |
[]=[]=[]=[E]
导入数据。列表
导入数据,也许吧
--每个e
                                  {-M A Z E-}
[E]=[ ]=[ ]=[ ] 
     |
[ ]=[ ]=[ ]=[ ]
     |       |
[ ] [ ]=[ ] [ ]
 |   |       |
[ ] [ ]=[ ]=[ ]
 |   |
[ ]=[ ]=[ ]=[E]

import Data.List
import Data.Maybe

--Each element in the maze lists the indexes of connected cells, '-1' for entrance, '-2' for exit
maze = [[-1,1],  [0,2,5],     [1,3],   [2],
        [5],     [4,6,1,9],   [5,7],   [6,11],
        [12],    [5,13,10],   [9],     [7,15],
        [8,16],  [14,9,17],   [13,15], [14,11],
        [12,17], [13,16,18],  [17,19], [18,-2]]

maze' = [[-1,1],  [0,2],    [1,3],   [2,7],
         [8,5],   [4,6],    [5,7],   [3,6],
         [4,9],   [8,10],   [9,11],  [10,15],
         [16,13], [12,14],  [13,15], [11,14],
         [12,17], [16,18],  [17,19], [18,-2]]

index a = fromJust $ elemIndex a maze
indexes a = map (index) a
areConnected index_a index_b = elem index_a (maze !! index_b)

isStart a  --(a :: cell)
  | elem (-1) a = True
  | otherwise   = False

isEnd a  --(a :: cell)
  | elem (-2) a = True
  | otherwise   = False

hasStart a   --(a :: [cell])
  | isStart (head a) = True
  | otherwise    = False

hasEnd a   --(a :: [cell])
  | isEnd (last a) = True
  | otherwise    = False

isSequenced (w:x:xs) (y:z:zs) --includes possibility of overlap since we do not know how many cells comprise the solution
  | areConnected (index $ last xs) (index y)
    || last xs == y
    || let (b:c:cs) = reverse (w:x:xs) in [c,b] == [y,z] = True
  | otherwise                                            = False

removeBacktracks (x:xs)
  | (x:xs) == []                                     = []
  | xs == []                                         = [x]
  | x == head xs                                     = removeBacktracks xs
  | length xs > 1 && x == let (y:ys) = xs in head ys = removeBacktracks (tail xs)
  | otherwise                                        = x : removeBacktracks xs

--list dead ends
dead_ends = filter (\x -> length x==1 && find (==(-1)) x == Nothing) maze
dead_ends_indexes = map (index) dead_ends

connectedToDeadEnd (x:xs)
  | x `elem` dead_ends_indexes                   = True
  | not (x `elem` dead_ends_indexes) && xs == [] = False
  | otherwise                                    = connectedToDeadEnd xs

--list first from dead ends
first_from_dead_ends = filter (\x -> length x==2 && find (==(-1)) x == Nothing && connectedToDeadEnd x) maze

--create sequences
filtered = [l | l <- maze, not (elem l dead_ends) && not (elem l first_from_dead_ends)]
sequences_3 = [[a,b,c] | a <- filtered, not (isEnd a),
                         b <- filtered, not (isEnd b || isStart b), areConnected (index a) (index b),
                         c <- filtered, not (isStart c), a /= c, areConnected (index b) (index c)]

sequences_4 = [a ++ [b] | a <- sequences_3, not (hasEnd a), b <- filtered,
                          last a /= b, areConnected (index $last a) (index b)]

paths = take 1 [indexes $ concat [a, b, c, d, e] | a <- sequences, hasStart a,
                                                   b <- sequences, not (hasStart b || hasEnd b),
                                                   isSequenced a b,
                                                   c <- sequences, b /= c, not (hasStart c || hasEnd c),
                                                   isSequenced b c,
                                                   d <- sequences, c /= d, not (hasStart d || hasEnd d),
                                                   isSequenced c d,
                                                   e <- sequences, hasEnd e,
                                                   isSequenced d e]
                                                 where sequences 
                                                         | length filtered < 16 = sequences_3
                                                         | otherwise            = sequences_4

path = removeBacktracks $ head paths
main = print path
--outputs: [0,1,5,9,13,17,18,19]