Algorithm Haskell中的动态规划记忆

Algorithm Haskell中的动态规划记忆,algorithm,haskell,Algorithm,Haskell,这是我第一次尝试使用(据我所知)动态规划。我试图解决这个有趣的问题: q函数尝试向后递归,跟踪模具的方向(visted从技术上讲是下一个单元,但就递归而言是“visted”,以防止无限的来回循环)。尽管我不确定它提供的答案是否是最好的解决方案,但它似乎确实提供了答案 我希望能想到如何实现某种形式的记忆来加速它——我试图用查找而不是来实现类似记忆化\u fib(参见)的东西,但没有成功,将q映射到(i,j)的组合列表,但没有得到任何内容,没有双关语 哈斯克尔代码: import Data.List

这是我第一次尝试使用(据我所知)动态规划。我试图解决这个有趣的问题:

q
函数尝试向后递归,跟踪模具的方向(
visted
从技术上讲是下一个单元,但就递归而言是“visted”,以防止无限的来回循环)。尽管我不确定它提供的答案是否是最好的解决方案,但它似乎确实提供了答案

我希望能想到如何实现某种形式的记忆来加速它——我试图用
查找
而不是
来实现类似
记忆化\u fib
(参见)的东西,但没有成功
,将
q
映射到
(i,j)
的组合列表,但没有得到
任何内容,没有双关语

哈斯克尔代码:

import Data.List (minimumBy)
import Data.Ord (comparing)

fst3 (a,b,c) = a

rollDie die@[left,right,top,bottom,front,back] move
  | move == "U" = [left,right,front,back,bottom,top]
  | move == "D" = [left,right,back,front,top,bottom]
  | move == "L" = [top,bottom,right,left,front,back]
  | move == "R" = [bottom,top,left,right,front,back]

dieTop die = die!!2

leftBorder = max 0 (min startColumn endColumn - 1)
rightBorder = min columns (max startColumn endColumn + 1)
topBorder = endRow
bottomBorder = startRow

infinity = 6*rows*columns

rows = 10
columns = 10

startRow = 1
startColumn = 1

endRow = 6
endColumn = 6

dieStartingOrientation = [4,3,1,6,2,5] --left,right,top,bottom,front,back

q i j visited 
  | i < bottomBorder || i > topBorder 
    || j < leftBorder || j > rightBorder = (infinity,[1..6],[])
  | i == startRow && j == startColumn    = (dieTop dieStartingOrientation,dieStartingOrientation,[])
  | otherwise                            = (pathCost + dieTop newDieState,newDieState,move:moves)
      where previous
              | visited == (i, j-1) = zip [q i (j+1) (i,j),q (i-1) j (i,j)] ["L","U"]
              | visited == (i, j+1) = zip [q i (j-1) (i,j),q (i-1) j (i,j)] ["R","U"]
              | otherwise           = zip [q i (j-1) (i,j),q i (j+1) (i,j),q (i-1) j (i,j)] ["R","L","U"]
            ((pathCost,dieState,moves),move) = minimumBy (comparing (fst3 . fst)) previous
            newDieState = rollDie dieState move

main = putStrLn (show $ q endRow endColumn (endRow,endColumn))
导入数据列表(minimumBy)
导入数据。Ord(比较)
fst3(a,b,c)=a
rollDie die@[左、右、上、下、前、后]移动
|移动==“U”=[左、右、前、后、下、上]
|移动==“D”=[左、右、后、前、上、下]
|移动==“L”=[上、下、右、左、前、后]
|移动==“R”=[底部、顶部、左侧、右侧、前部、后部]
dieTop die=die!!2.
leftBorder=最大值0(最小开始列结束列-1)
rightBorder=最小列数(最大开始列数+结束列数+1)
topBorder=endRow
bottomBorder=startRow
无穷大=6*行*列
行=10
列=10
startRow=1
startColumn=1
endRow=6
endColumn=6
模具开始方向=[4,3,1,6,2,5]--左、右、上、下、前、后
q i j访问
|itopBorder
||jrighborder=(无穷大,[1..6],)
|i==startRow&&j==startColumn=(dieTop dieStartingOrientation,dieStartingOrientation,[]))
|否则=(pathCost+dieTop-newDieState,newDieState,move:moves)
以前在哪里
|到访==(i,j-1)=zip[qi(j+1)(i,j),q(i-1)j(i,j)][“L”,“U”]
|到访==(i,j+1)=zip[qi(j-1)(i,j),q(i-1)j(i,j)][“R”,“U”]
|否则=zip[qi(j-1)(i,j),qi(j+1)(i,j),q(i-1)j(i,j)][“R”,“L”,“U”]
((路径成本,死亡状态,移动),移动)=最小值(比较(fst3.fst))上一个
newDieState=滚动Die dieState移动
main=putStrLn(显示$q endRow endColumn(endRow,endColumn))

我解决此类问题的工具是库

要使用它,只需导入
数据.memobombinators
,将
q
重命名为其他名称,例如
q'
(但保持递归调用不变),然后定义一个新的
q
,如下所示:

q = M.memo3 M.integral M.integral (M.pair M.integral M.integral) q'
  • memo3
    为一个三参数函数创建一个备忘录,为每个参数指定备忘录
  • integral
    是一个简单的整数类型记忆工具
  • pair
    组合两个记忆器,为这些类型的成对记忆器创建一个记忆器
  • 最后,我们将此记忆器应用于
    q'
    ,以获得一个记忆版本
就这样。您的函数现在已被记忆。测试时间:

> :set +s
> q endRow endColumn (endRow,endColumn)
(35,[5,2,4,3,6,1],["R","R","R","R","R","U","U","U","U","U"])
(0.01 secs, 516984 bytes)
完整代码如下:


导入数据列表(minimumBy)
导入数据。Ord(比较)
导入符合条件的数据.M作为M
fst3(a,b,c)=a
rollDie die@[左、右、上、下、前、后]移动
|移动==“U”=[左、右、前、后、下、上]
|移动==“D”=[左、右、后、前、上、下]
|移动==“L”=[上、下、右、左、前、后]
|移动==“R”=[底部、顶部、左侧、右侧、前部、后部]
dieTop die=die!!2.
leftBorder=最大值0(最小开始列结束列-1)
rightBorder=最小列数(最大开始列数+结束列数+1)
topBorder=endRow
bottomBorder=startRow
无穷大=6*行*列
行=10
列=10
startRow=1
startColumn=1
endRow=6
endColumn=6
模具开始方向=[4,3,1,6,2,5]--左、右、上、下、前、后
q=M.3 M.积分M.积分(M.对M.积分M.积分)q'
哪里
q'i'j访问
|itopBorder | | jrighborder=(无穷大,[1..6],)
|i==startRow&&j==startColumn=(dieTop dieStartingOrientation,dieStartingOrientation,[]))
|否则=(pathCost+dieTop-newDieState,newDieState,move:moves)
以前在哪里
|到访==(i,j-1)=zip[qi(j+1)(i,j),q(i-1)j(i,j)][“L”,“U”]
|到访==(i,j+1)=zip[qi(j-1)(i,j),q(i-1)j(i,j)][“R”,“U”]
|否则=zip[qi(j-1)(i,j),qi(j+1)(i,j),q(i-1)j(i,j)][“R”,“L”,“U”]
((路径成本,死亡状态,移动),移动)=最小值(比较(fst3.fst))上一个
newDieState=滚动Die dieState移动
main=putStrLn(显示$q endRow endColumn(endRow,endColumn))

我想如果你发布了你的尝试,但没有成功,那会有所帮助。我很久以前在Haskell花了很多时间来思考回忆录的问题。我记不起细节,但最终我成功了(我想,它可能还有其他问题,比如空间泄漏),定义了一个数组实例,以便根据其他数组元素计算任何给定索引的值。然后,懒惰的评估似乎迫使所有数组元素按正确的顺序“填充”,这似乎有点不可思议(尽管我感到欣慰而不是高兴)。在数据结构“leads”下,函数“follow”。@j_random_hacker请检查应用的骰子算法——2.13秒内300x300,没有表格,并且比Paul的a*小,酷还是什么?谢谢我用这个包做了实验,但不知道如何解释我的q函数类型。
import Data.List (minimumBy)
import Data.Ord (comparing)
import qualified Data.MemoCombinators as M

fst3 (a,b,c) = a

rollDie die@[left,right,top,bottom,front,back] move
  | move == "U" = [left,right,front,back,bottom,top]
  | move == "D" = [left,right,back,front,top,bottom]
  | move == "L" = [top,bottom,right,left,front,back]
  | move == "R" = [bottom,top,left,right,front,back]

dieTop die = die!!2

leftBorder = max 0 (min startColumn endColumn - 1)
rightBorder = min columns (max startColumn endColumn + 1)
topBorder = endRow
bottomBorder = startRow

infinity = 6*rows*columns

rows = 10
columns = 10

startRow = 1
startColumn = 1

endRow = 6
endColumn = 6

dieStartingOrientation = [4,3,1,6,2,5] --left,right,top,bottom,front,back

q = M.memo3 M.integral M.integral (M.pair M.integral M.integral) q'
  where
    q' i j visited 
      | i < bottomBorder || i > topBorder || j < leftBorder || j > rightBorder = (infinity,[1..6],[])
      | i == startRow && j == startColumn    = (dieTop dieStartingOrientation,dieStartingOrientation,[])
      | otherwise                            = (pathCost + dieTop newDieState,newDieState,move:moves)
      where previous
              | visited == (i, j-1) = zip [q i (j+1) (i,j),q (i-1) j (i,j)] ["L","U"]
              | visited == (i, j+1) = zip [q i (j-1) (i,j),q (i-1) j (i,j)] ["R","U"]
              | otherwise           = zip [q i (j-1) (i,j),q i (j+1) (i,j),q (i-1) j (i,j)] ["R","L","U"]
            ((pathCost,dieState,moves),move) = minimumBy (comparing (fst3 . fst)) previous
            newDieState = rollDie dieState move

main = putStrLn (show $ q endRow endColumn (endRow,endColumn))