Search code examples
haskellmemoizationpascals-triangle

Why is this memoized Pascal's Triangle recursion not as fast as expected?


Referencing this article on Memoization, I believe that this approach uses memoization, and should be fast. However, it doesn't seem to be the case here:

pascal :: Int -> Int -> Integer
pascal a = (map ((map pascal' [0..]) !! a) [0..] !!)
  where pascal' a b | a == 1 && b == 1  = 1
                    | a <= 0 || b <= 0  = 0 
                    | otherwise         = pascal (a-1) (b-1) + pascal (a-1) b 

How does Haskell interpret this? How to make this function faster?


Solution

  • You need to separate the construction of the (complete) data structure from the selection of the correct element within the data structure.

    That is, you need to define the whole Pascal triangle:

    triangle :: [[Integer]]
    triangle = [[pascal' a b | b <- [0..]] | a <- [0..]]
      where pascal' a b | a == 1 && b == 1  = 1
                        | a <= 0 || b <= 0  = 0
                        | otherwise         = triangle !! (a-1) !! (b-1) 
                                              + triangle !! (a-1) !! b
    

    and then selection from the triangle will be memoized:

    > triangle !! 100 !! 50
    50445672272782096667406248628
    >
    

    After the data structure is defined and given a name, you can define a function for accessing the elements:

    pascal :: Int -> Int -> Integer
    pascal a b = triangle !! a !! b
    

    giving the memoized call:

    > pascal 100 50
    50445672272782096667406248628
    

    You can move triangle into the where clause, giving the complete code:

    pascal :: Int -> Int -> Integer
    pascal a b = triangle !! a !! b
      where
        triangle = [[pascal' a b | b <- [0..]] | a <- [0..]]
        pascal' a b | a == 1 && b == 1  = 1
                    | a <= 0 || b <= 0  = 0
                    | otherwise         = triangle !! (a-1) !! (b-1)
                                          + triangle !! (a-1) !! b
    

    which should work fine, even when compiled unoptimized or loaded into GHCi.