【问题标题】:My "memoized" pascal function doesn't really work我的“记忆”帕斯卡函数并没有真正起作用
【发布时间】:2020-10-02 05:16:50
【问题描述】:
import Data.List (intercalate)
import Control.Concurrent (threadDelay)
import Data.Maybe (fromJust)
import System.IO


-- I love how amazingly concise Haskell code can be.  This same program in C, C++ or Java
-- would be at least twice as long.


pascal :: Int -> Int -> Int
pascal row col | col >= 0 && col <= row =
                 if row == 0 || col == 0 || row == col
                 then 1
                 else pascal (row - 1) (col - 1) + pascal (row - 1) col
pascal _ _ = 0


pascalFast' :: [((Int, Int), Int)] -> Int -> Int -> Int
pascalFast' dict row col | col > row = 0
pascalFast' dict row col | row == 0 || col == 0 || row == col = 1
pascalFast' dict row col =
  let value1 = lookup (row - 1, col - 1) dict
      value2 = lookup (row - 1, col) dict
  in if not(value1 == Nothing || value2 == Nothing)
     then (fromJust value1) + (fromJust value2)
     else let dict'  = ((row - 1, col), pascalFast' dict (row - 1) col) : dict
              dict'' = ((row - 1, col - 1), pascalFast' dict' (row - 1) (col - 1)) : dict' 
          in (pascalFast' dict'' (row - 1) col) + (pascalFast' dict'' (row - 1) (col - 1))


pascalFast = pascalFast' []
                

pascalsTriangle :: Int -> [[Int]]
pascalsTriangle rows =
  [[pascal row col | col <- [0..row]] | row <- [0..rows]]


main :: IO ()
main = do
  putStrLn "" 
  putStr "Starting at row #0, how many rows of Pascal's Triangle do you want to print out? "
  hFlush stdout
  numRows <- (\s -> read s :: Int) <$> getLine
  let triangle = pascalsTriangle numRows
      longestStringLength = (length . show) $ foldl1 max $ flatten triangle
      triangleOfStrings = map (intercalate ", ") $ map (map (pad longestStringLength)) triangle
      lengthOfLastDiv2 = div ((length . last) triangleOfStrings) 2 
  putStrLn ""
  mapM_ (\s -> let spaces = [' ' | x <- [1 .. lengthOfLastDiv2 - div (length s) 2]]
                   in (putStrLn $ spaces ++ s) >> threadDelay 200000) triangleOfStrings
  putStrLn ""
  

flatten :: [[a]] -> [a]
flatten xs =
  [xss | ys <- xs, xss <- ys]


pad :: Int -> Int -> String
pad i k =
  [' ' | _ <- [1..n]] ++ m
  where m = show k
        n = i - length m

对于我的生活,我不明白为什么 pascalFast 不是 FAST !!!它类型检查并且在数学上它是正确的,但是我的“pascalFast”函数和我的“pascal”函数一样慢。有任何想法吗?不,这不是家庭作业。这是我只想为自己尝试的东西。感谢您的反馈。

最好, 道格拉斯·勒维特

【问题讨论】:

    标签: haskell memoization


    【解决方案1】:

    您的 main 实际上根本没有调用 pascalFast,所以我不清楚您到底在做什么导致您得出结论它很慢 - 通过一些努力,我可以看出它看起来很慢在它,但问题中的一些证据会很好。

    至于为什么,我突然想到了两个问题。在我看来,因为您将字典“向上”传递给基本情况,但从不向下或横向传递,您只是在缓存永远不会再查看的结果。尝试在纸上手动评估pascalFast [] 2 1,看看是否有缓存命中。

    其次,即使您正确缓存,使用 lookup 所花费的时间与列表大小呈线性关系,因此您的运行时间至少与生成的条目数成二次方:对于您生成的每个项目,您查看所有其他项目至少一次。要有效地缓存,您需要一个真实的数据结构,例如来自 Data.Map 的数据结构。

    但与如何有​​效记忆的问题分开,通常最好根本不记忆,从基本案例开始并建立,而不是从最终结果开始。对于帕斯卡三角形来说,这样的事情非常经典:

    triangle :: [[Int]]
    triangle = iterate nextRow [1]
      where nextRow xs = 1 : zipWith (+) xs (tail xs) ++ [1]
    
    main :: IO ()
    main = print $ take 5 triangle
    

    【讨论】:

    • 哦,我知道我不会在 main 中调用 pascalFast。我用 GHCi 对其进行了测试,这就是我知道它的运行时性能并不比 unmemoized “pascal” 函数更好的方式。但感谢您的帮助。你给了我一些值得思考的好东西。
    猜你喜欢
    • 2012-07-13
    • 1970-01-01
    • 2021-12-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-12-24
    • 1970-01-01
    相关资源
    最近更新 更多