Generating Fibonacci numbers in Haskell?

前端 未结 11 975
有刺的猬
有刺的猬 2020-11-29 18:35

In Haskell, how can I generate Fibonacci numbers based on the property that the nth Fibonacci number is equal to the (n-2)th Fibonacci number plus the (n-1)th Fibonacci numb

相关标签:
11条回答
  • 2020-11-29 19:17
    fibs :: [Integer]
    fibs = 1 : 1 : zipWith (+) fibs (tail fibs)
    

    at first, with fibs and tail fibs, we can get the 3rd element:

    fibs                        : [1, 1, ?
    tail fibs                   : [1, ?
    zipWith (+) fibs (tail fibs): [2, ?
    

    now, we know the 3rd is 2, we can get the 4th:

    fibs                        : [1, 1, 2, ?
    tail fibs                   : [1, 2, ?
    zipWith (+) fibs (tail fibs): [2, 3, ?
    

    now the 5th:

    fibs                        : [1, 1, 2, 3, ?
    tail fibs                   : [1, 2, 3, ?
    zipWith (+) fibs (tail fibs): [2, 3, 5, ?
    

    and so on ..

    0 讨论(0)
  • 2020-11-29 19:17

    Put in code, your definition is

    fib :: Int -> Integer
    fib 0 = 1
    fib 1 = 1
    fib n = fib (n-1) + fib (n-2)
      -- i.e.
      -- fib (n+2) = fib (n+1) + fib n
    

    Int -> a ~= [a] because

    from f = map f [0..]     -- from :: (Int -> a) -> [a]
    to = (!!)                -- to :: [a] -> (Int -> a)
    

    Thus

    fibs :: [Integer]
    fibs = from fib 
    
    fibs !! 0 = 1
    fibs !! 1 = 1
    fibs !! (n+2)    = fibs !! (n+1)     +  fibs !! n
    -- or,
    drop 2 fibs !! n = drop 1 fibs !! n  +  fibs !! n
                     = zipWith (+) (tail fibs) fibs !! n
    -- i.e.
    take 2 fibs = [1,1]
    drop 2 fibs = zipWith (+) (tail fibs) fibs
    -- hence, 
    fibs = take 2 fibs ++ drop 2 fibs
         = 1 : 1 : zipWith (+) (tail fibs) fibs
    

    Or, as a, b = (0,1) : (b, a+b):

    fibs :: [Integer]
    fibs = a
      where
      (a,b) = unzip $ (0,1) : zip b (zipWith (+) a b)
    
    0 讨论(0)
  • 2020-11-29 19:19

    A lazy way of generating infinite Fibonacci series can easily be achieved by unfoldr as follows;

    fibs :: [Integer]
    fibs = unfoldr (\(f,s) -> Just (f,(s,f+s))) (0,1)
    
    0 讨论(0)
  • 2020-11-29 19:20

    To expand on dtb's answer:

    There is an important difference between the "simple" solution:

    fib 0 = 1
    fib 1 = 1
    fib n = fib (n-1) + fib (n-2)
    

    And the one you specified:

    fibs = 1 : 1 : zipWith (+) fibs (tail fibs)
    

    The simple solution takes O(1.618NN) time to compute the Nth element, while the one you specified takes O(N2). That's because the one you specified takes into account that computing fib n and fib (n-1) (which is required to compute it) share the dependency of fib (n-2), and that it can be computed once for both to save time. O(N2) is for N additions of numbers of O(N) digits.

    0 讨论(0)
  • 2020-11-29 19:31

    LOL, I love Haskell pattern matching but it is rendered useless in standard Fibonacci functions. The standard list is constructed from the right. To use pattern matching and cons, the list must be constructed from the left. Well, one consolation, at least, is this is really fast. ~O(n), it should be. A helper function is needed to reverse the infinite list (things you can only do in Haskell, joy) and this function outputs each subsequent list of the run so 'last' is also used in the helper function pipeline.

    f (x:y:xs) = (x+y):(x:(y:xs))
    

    The helper

    fib n = reverse . last . take n $ iterate f [1,0]
    

    This is a list version and, I think, it explicates how the list is constructed which is the purpose. I want to do a tuple version.

    Edit 3/15/2018

    First off, Will Ness enlightened me with the knowledge that an entire list being generated at each iteration was unnecessary and that only the last two values used were needed and that the values for the result list were the first values of each list or pair generated. It was so funny. After Will told me the values for the list were the first values of the lists, I ran it and saw the values 0,1,1,2,3,5,8,13 as each head of each list, I said WTF, did Will change my code on my PC? The values were there but how!? After a while, I realized they were there all along but I just didn't see them. ugh. Will's version of the function and helper function are:

    f = (\(x:y:xs) -> (x+y):x:xs) -- notice, no y: put back only x+y & x
    

    and his helper function rewrite

    fib n = map head . take n $iterate f [0,1]
    

    I think, too, that they now can be combined:

    fib n = take n . map head $ iterate (\(x:y:xs) -> (x+y):x:xs) [0,1]
    

    As an irrelevant aside, the function can be with tuples, too

    fib n = take n . map fst $ iterate (\(a,b) -> (b,a+b)) (0,1)
    

    Another form, a list comprehension form, can also be written for all:

    fib n = take n [ fst t | t <- iterate (\(a,b) -> (b,a+b)) (0,1)]
    

    These are all iterative and robust. The fastest is the map with lists at 12.23 seconds for fib 5000. The tuple comprehension was second fastest for fib 5000 at 13.58 seconds.

    0 讨论(0)
提交回复
热议问题