问题描述
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”功能一样慢。有任何想法吗?不,这不是家庭作业。我只是想自己尝试一下。感谢您的反馈。
最好, 道格拉斯·莱威特
解决方法
您的main
实际上根本没有调用pascalFast
,因此我不清楚您在做什么,这导致您断定它很慢-尽力而为,我可以告诉它观察起来很慢,但是问题中的一些证据会很好。
为什么,我遇到了两个问题。在我看来,由于您将字典“向上”传递给基本情况,而从不向下或向侧面传递,因此您只是在缓存结果,您将永远不会再看。尝试在纸上手工评估pascalFast [] 2 1
,看看是否遇到过缓存问题。
第二,即使您正确地缓存了,使用lookup
的时间也会与列表大小成线性关系,因此您的运行时至少要生成二次项:对于您生成的每个项目,您都会至少一次查看所有其他项目。为了有效地缓存,您需要一个真实的数据结构,例如Data.Map
中的结构。
但是,除了如何有效记忆的问题之外,通常最好根本不记忆,通过从基础案例开始并逐步积累,而不是从最终结果中得出。对于Pascal的Triangle,类似这样的东西非常经典:
triangle :: [[Int]]
triangle = iterate nextRow [1]
where nextRow xs = 1 : zipWith (+) xs (tail xs) ++ [1]
main :: IO ()
main = print $ take 5 triangle