我正在尝试从Project Euler解决#15,这是我的第一个解决方案 import Data.List
type Location = (Int,Int)
boardX = 20
boardY = 20
stepBack :: Location -> [Location]
stepBack (x,y) = [(x-1,y), (x,y-1)]
legalStep :: Location -> Bool
legalStep (x,y) = x >= 0 && y >= 0
iteractStep :: Int -> [[Location]]
iteractStep 0 = [[(boardX, boardY)]]
iteractStep n = [x:y|y <- iteractStep (n-1), x<- stepBack (head y), legalStep x]
main :: IO ()
main = putStrLn $ show $ length $ iteractStep (boardX + boardY)
这很慢,我发现有很多子问题重新计算,我试着记住子问题的答案,然后我找到了这个例子:
memoized_fib :: Int -> Integer
memoized_fib = (map fib [0 ..] !!)
where fib 0 = 0
fib 1 = 1
fib n = memoized_fib (n-2) + memoized_fib (n-1)
我在ghci中尝试它,我发现它实际上记住了子问题的结果,我无法理解这段代码如何记住结果,它看起来像
map fib [0 ..]
是全局的,但我认为它是方法范围(仅存在于每个方法调用中),为什么它可以记住结果?
答案 0 :(得分:2)
memoized_fib
有效,因为它最初只是在thunk
中创建map fib [0..]
(这是懒惰,需要呼叫)。在后续调用中,计算列表中的fib
值越来越多,同时每个调用通过索引到列表中来查找fib
值(!!)。
如果没有列表,则每次调用fib
都会再次调用fib
两次,这会再次进行两次调用,等等。在算法术语中,这会从{O}更改fib
2)到O(n)。
如果没有查看Project Euler问题,我建议您首先尝试使用其他数据结构。在您的情况下,您应该尝试使用vector或array。这些都比你当前使用的列表快得多,实际上是一个链表。
您可能还会将您的电路板表示更改为board :: [Int]
。我们直观地认为电路板有两个维度,但它很容易实现为列表而不是列表列表。 (我记得PAIP)
对于其他问题,请牢记containers和unordered-containers。