54

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 number?

I've seen this:

fibs :: [Integer]
fibs = 1 : 1 : zipWith (+) fibs (tail fibs)

I don't really understand that, or how it produces an infinite list instead of one containing 3 elements.

How would I write haskell code that works by calculating the actual definition and not by doing something really weird with list functions?

Adam Stelmaszczyk
  • 18,835
  • 4
  • 63
  • 104
Lucky
  • 4,517
  • 7
  • 38
  • 50
  • 11
    You're missing all the fun of Haskell if you avoid the "weird" list functions. But for what it's worth, there's a good explanation of how the recursion works in the above code here: http://scienceblogs.com/goodmath/2006/11/simple_functions_in_haskell_1.php – rtperson Jul 09 '09 at 20:58
  • 5
    The article @rtperson links to is now at http://scienceblogs.com/goodmath/2006/11/28/simple-functions-in-haskell-1/ . – Ben Jan 02 '16 at 23:57
  • There is an alternative Haskell definition for the Fibonacci serie, which would be easier to analyze I think: | `fibSerie a b = a : (fibSerie b (a+b))` and then: `fibs = fibSerie 1 1`. – jpmarinier Jul 25 '19 at 21:35
  • `ω = 2 + min ω (ω - 1)`. `zipWith` produces an (infinite) list of integers here, not just one integer, so it's not `2 + 1` overall elements, but `2 + ω`. which is [`ω`](https://en.wikipedia.org/wiki/Ordinal_number). – Will Ness Aug 20 '19 at 09:17

11 Answers11

97

Here's a different and simpler function that calculates the n'th Fibonacci number:

fib :: Integer -> Integer
fib 0 = 0
fib 1 = 1
fib n = fib (n-1) + fib (n-2)

The implementation you are referring to relays on some observations about how values in Fibonacci relate to each other (and how Haskell can define data structures in terms of themselfs in effect creating infinite data structures)

The function in your question works like this:

Assume you already had an infinite list of the Fibonacci numbers:

   [ 1, 1, 2, 3, 5,  8, 13, .... ]

The tail of this list is

   [ 1, 2, 3, 5, 8, 13, 21, .... ]

zipWith combines two lists element by element using the given operator:

   [ 1, 1, 2, 3,  5,  8, 13, .... ]
+  [ 1, 2, 3, 5,  8, 13, 21, .... ]
=  [ 2, 3, 5, 8, 13, 21, 34, .... ]

So the infinite list of Fibonacci numbers can be calculated by prepending the elements 1 and 1 to the result of zipping the infinite list of Fibonacci numbers with the tail of the infinite list of Fibonacci numbers using the + operator.

Now, to get the n'th Fibonacci number, just get the n'th element of the infinite list of Fibonacci numbers:

fib n = fibs !! n

The beauty of Haskell is that it doesn't calculate any element of the list of Fibonacci numbers until its needed.

Did I make your head explode? :)

przemo_li
  • 3,703
  • 3
  • 31
  • 52
dtb
  • 198,715
  • 31
  • 379
  • 417
  • 30
    I love that - calculate the list by summing the corresponding values of the list you're trying to figure out. My brain doesn't ordinarily work like that - it's like trying to look inside your own ear. – Steve B. Jul 09 '09 at 19:04
  • 2
    `fib 0 = 1` should be `fib 0 = 0`. I only noticed this because I just this second made the same mistake. Haha. – Christopher Done Feb 05 '10 at 21:01
  • 3
    @Christopher sometimes the first 0 of the sequence is omitted. – Yacoby Mar 12 '10 at 12:40
  • @Yacoby His definition above is incorrect. fib(0)=0, not 1. This is important. fib(2)=1, the definition above yields fib(2)=2. This is because fib(n) = fib(n-1) + fib(n-2). So, by the above definition, fib(2) = fib(1) + fib(0) = 1 + 1 = 2, which is wrong. This screws up the whole sequence. – Christopher Done Mar 12 '10 at 22:28
  • 2
    @Christoper No it doesn't. The sequence is just shifted left by 1. Not really a big deal. – Yacoby Mar 13 '10 at 00:30
  • @dtb am I right in assuming this works by utilising tail recursion? – abarax Sep 05 '11 at 00:58
  • 4
    @Abarax No, in fact tail recursion would make the trick impossible. It's laziness and guarded recursion, the recursive call is in each step in a constructor field, `fibo : recursive_call`, so to reach it, we have to deconstruct the result of the previous call. Thus the recursion depth is never larger than 1. – Daniel Fischer Jan 19 '12 at 06:17
  • It's worth mentioning that this definition does more additions than is necessary: O(fib n) additions. The zipWith solution only needs O(n). – occulus Dec 28 '12 at 21:55
  • Whoops, I see yairchu has covered this below. – occulus Dec 28 '12 at 21:56
  • I still don't get where Haskell gets the infinite lists from in the first place, even if only calculating the first Fibonacci number. I am using two endless lists now, that much I understand and adding them is not very complex, but where do they come from? I'd still have to calculate them, which would take about the same time as calculating the Fibonacci number with simple recursion – Where am I missing something here? – Zelphir Kaltstahl Feb 07 '16 at 00:39
  • @Zelphir "which would take about the same time as calculating the Fibonacci number with simple recursion" untrue, as calculating fibonacci with recursion does O(fib n) additions which is MASSIVE (exponential). The advantage of the list method is that consecutive calls to the same fibonacci value (the `fib(3)` in `fib(2) + fib(3), fib(3) + fib(4)`) are essentially cached in the infinite list itself, so it becomes more of a memoized recursive function, which involves O(n) additions. – semicolon Feb 10 '16 at 16:31
  • @semicolon I get that O(n) and O(fib n) argument, but that assumes you already have an endless list. So where do you get that infinite list from? This all sounds like there is something already prepared somewhere in Haskell, but seems like there isn't, otherwise I'd simply use that infinite list and get the nth element. How would you calculate the infinite lists then and still stay below O(fib n)? – Zelphir Kaltstahl Feb 10 '16 at 23:54
  • 3
    @Zelphir You are generating the infinite list with `0 : 1 : zipWith (+) fibs (tail fibs)`. You start with `[0, 1...]` and append `zipWith (+) fibs (tail fibs)` to it. The first element of fibs is `0` and the first element of tail fibs is `10 so the next element is `0 + 1 = 1` giving you `[0, 1, 1...]` and now you get the second element of `zipWith ...` which is `1 + 1 = 2` giving you `[0, 1, 1, 2...]` and so on. – semicolon Feb 11 '16 at 00:27
  • @semicolon Thanks, I think I get it now after reading http://scienceblogs.com/goodmath/2006/11/28/simple-functions-in-haskell-1/ . Before I didn't understand what you wrote, but now it's clearer: The trick is in those two already existing elements 0 and 1 and then always appending, to save previous calculation results. I guess this is again a space vs time tradeoff. Either use more memory to keep all the previously calculated values, or calculate again and again with another approach (not the naive one with O(fib n)). – Zelphir Kaltstahl Feb 11 '16 at 00:47
  • For the ones who go through this comments and still are puzzled, [this answer to a follow up question](https://stackoverflow.com/a/57202912/5825294) could be useful. – Enlico Feb 12 '20 at 18:00
  • Excellent answer. – Iceland_jack Nov 09 '20 at 13:45
  • This function do not handle negative numbers as an input. – Yan.F Apr 06 '21 at 20:39
28

going by the definition, every item of the fibonacci series is the sum of the previous two terms. putting this definition in to lazy haskell gives u this!

fibo a b = a:fibo b (a+b)

now just take n items from fibo starting with 0,1

take 10 (fibo 0 1)
renjith
  • 461
  • 1
  • 5
  • 7
  • 1
    i.e. [`a, b = (0,1) : (b, a+b)`](http://en.wikipedia.org/wiki/Corecursion#Fibonacci_sequence) or in Haskell, `map fst $ ((\(a,b)->(b,a+b)) `iterate` (0,1))`. :) – Will Ness Feb 06 '14 at 21:38
  • for `fibs = map fst $ iterate (\(a,b) -> (b,a+b)) (0,1)` see https://wiki.haskell.org/The_Fibonacci_sequence#With_iterate – Wolf Jul 17 '17 at 10:08
  • What is the computational complexity compared to `fibs = 0 : 1 : zipWith (+) fibs (tail fibs)` ? – Wolf Jul 17 '17 at 10:34
  • That is one beautiful function and beauty is everything in math and programming. The simplicity and cogency is remarkable. It is poetic, compact and full of meaning. – fp_mora Mar 07 '18 at 00:02
22

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.

Community
  • 1
  • 1
yairchu
  • 21,122
  • 7
  • 65
  • 104
  • @newacct: If you only want "fibs !! n", you need to calculative all of "take n fibs", n items, with a calculation of O(n) each because adding two numbers of O(n) digits is O(n). – yairchu Jul 10 '09 at 05:31
  • 1
    @newacct: You're assuming that every distinct dynamic occurrence of "fib k" (where k is a constant) is merged into a single thunk. GHC might be smart enough to do that in this case, but I don't think it's guaranteed. – Chris Conway Jul 10 '09 at 14:54
  • okay i misread the question. i see that you already said what i was trying to say – newacct Jul 10 '09 at 18:07
  • 2
    Why not simply say the golden ratio (Phi) instead of imprecise `1.618`? – Zelphir Kaltstahl Feb 11 '16 at 01:45
  • 2
    @Zelphir: that would require readers to also be familiar with the golden ratio. Preciseness isn't critical to this argument – yairchu Feb 15 '16 at 11:45
5

There are a number of different Haskell algorithms for the Fibonacci sequence here. The "naive" implementation looks like what you're after.

Richard Dunlap
  • 1,855
  • 10
  • 14
4
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 ..

nichijou
  • 347
  • 1
  • 9
3

The definition of fibonaci(n) is:

fibonacci (n) = fibonacci (n-1) + fibonacci (n-2)

The naive implementation in Haskell

fibonacci :: Integer -> Integer
fibonacci 0 = 1
fibonacci 1 = 1
fibonacci x = fibonacci (x-1) + fibonacci (x-2)

All formulas can be traced back to this definition, some which run very quickly, some of which run very slowly. The implementation above has O(n) = 2^n

In the spirit of your question, let me remove the use of lists and give you something that runs in O(n) I.e. let's not hold all the fibonaccis from 0 to n in a list.

If we have a triple (a tuple with three members) that looks like:

(n, fibonacci[n-1], fibonacci[n])

Remembering the initial definition, we can calculate the next triple from the last triple:

(n+1, fibonacci[n], fibonacci[n-1] + fibonacci[n]) = (n+1, fibonacci[n], fibonacci[n+1])

And the next triple from the last triple: (n+2, fibonacci[n+1], fibonacci[n] + fibonacci[n+1]) = (n+1, fibonacci[n+1], fibonacci[n+2])

And so on...

n = 0 => (0,0,1) 
n = 1 => (1,1,1) - calculated from the previous triple
n = 2 => (2,1,2) - calculated from the previous triple
n = 3 => (3,2,3) - calculated from the previous triple
n = 4 => (4,3,5) - calculated from the previous triple
n = 5 => (5,5,8) - calculated from the previous triple

Let's implement this in Haskell and use self explanatory variable names:

nextTripleIfCurrentNIsLessThanN :: (Int, Integer, Integer) -> Int -> (Int, Integer, Integer)
nextTripleIfCurrentNIsLessThanN (currentN, x, y) n = if currentN < n
then nextTripleIfCurrentNIsLessThanN (currentN + 1, y, x + y) n
else (currentN, x, y)

thirdElementOfTriple :: (x,y,z) -> z
thirdElementOfTriple (x,y,z) = z

fibonacci :: Int -> Integer
fibonacci n = thirdElementOfTriple (nextTripleIfCurrentNIsLessThanN (0,0,1) n)

This will work in O(n) [It is mildly quadratic which shows up in large numbers. The reason for that is that adding big numbers is more costly than adding small ones. But that's a separate discussion about model of computation.]

fibonacci 0
1
fibonacci 1
1
fibonacci 2
2
fibonacci 3
3
fibonacci 4
5
fibonacci 5
8
fibonacci 5000
6276302800488957086035253108349684055478528702736457439025824448927937256811663264475883711527806250329984690249846819800648580083040107584710332687596562185073640422286799239932615797105974710857095487342820351307477141875012176874307156016229965832589137779724973854362777629878229505500260477136108363709090010421536915488632339240756987974122598603591920306874926755600361865354330444681915154695741851960071089944015319300128574107662757054790648152751366475529121877212785489665101733755898580317984402963873738187000120737824193162011399200547424034440836239726275765901190914513013217132050988064832024783370583789324109052449717186857327239783000020791777804503930439875068662687670678802914269784817022567088069496231111407908953313902398529655056082228598715882365779469902465675715699187225655878240668599547496218159297881601061923195562143932693324644219266564617042934227893371179832389642895285401263875342640468017378925921483580111278055044254198382265567395946431803304304326865077742925818757370691726168228648841319231470626
galeaspablo
  • 769
  • 4
  • 15
1

using iterate

fibonaci = map fst (iterate f (0,1)) where f (x,y) = (y,x+y)

using

take 10 fibonaci

[0,1,1,2,3,5,8,13,21,34,55,89,144,233,377]
jmejia
  • 21
  • 2
1

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)
Redu
  • 19,106
  • 4
  • 44
  • 59
0

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.

fp_mora
  • 580
  • 5
  • 9
  • haskell lists can be constructed from the top (left) though just as easily, with guarded recursion (i.e. thanks to the laziness; e.g. [this answer](https://stackoverflow.com/a/21605696/849891)). `last . take n` is just `(!! (n-1))`. with your `fib`, `fib n` doesn't help to find `fib (n+1)` as much as we'd want. just define instead `fibs = map head $ iterate f [1,0]` and then `fib n = fibs !! n`. Now we discover that it creates a whole list on each step but uses only 2 of its head elements, so we change it to `fibs = map fst $ iterate g (1,0)` with `f` correspondingly changed, into `g`. voila. – Will Ness Mar 12 '18 at 14:20
  • It takes real vision to see that the head of each list generated were the numbers desired. I lack that vision. Thank you so very much, This lesson extends well beyond this problem and your piercing insight into it. That said, I take map fst $ iterate g (1,0) as delightful humor. The tuple version is indeed to replace f Also in "fibs = map head $ iterate f [1,0]" using [0,1] as a parameter results in 0 as the head of the output list of "take n $ map head $ iterate f [0,1]' I have no working concept of the tuple version, yet and yes, laziness in a language is better than ice cream. Almost. – fp_mora Mar 13 '18 at 16:51
  • try `mapM_ print $ take 15 $ iterate f [1,0]`. Now change `f` to `f (x:y:xs) = (x+y):(x:xs)` and try that `mapM_ ...` line again and compare the outputs. – Will Ness Mar 13 '18 at 17:23
  • want to be blown away by laziness, try `ps n = q where q = scanl (\\) [2..n] [[p,p+p..n] | p – Will Ness Mar 13 '18 at 17:56
  • @Will Ness is a wizard He improved my sorry code with "f (x:y:xs) = (x+y):(x:xs)" which is much cleaner. His reworking of the helper function is "map head $ take 24 $ iterate f [0,1]" which is also very much cleaner Haskell's laziness prevents any performance penalty for expressive clarity. I am a Haskell neophyte so cherish this site & wonderful people B/c of Will Ness, I just used a monad & soon will get to explore the '\\' operator & scanl which I also have never done Will Ness, what I was really looking for was f . f . f ... f (x) Using the Y combinator It should be sweet – fp_mora Mar 13 '18 at 17:57
  • Hi, I'm not a wizard, far from it, I've just been playing with it for a long time. `y g = g (y g)` works. `Data.Function` defines `fix f = x where { x = f x }`, which is very similar, but [vastly different](https://stackoverflow.com/a/13029529/849891). – Will Ness Mar 13 '18 at 17:59
  • From the Haskell Wiki, a major difference between the two Y functions """"""The recursion can be replaced with fix : fibs = fix (scanl (+) 0 . (1:)) fibs = fix ((0:) . scanl (+) 1) The fix used here has to be implemented through sharing, fix f = xs where xs = f xs, not code replication, fix f = f (fix f), to avoid quadratic behaviour.""" – fp_mora Mar 13 '18 at 21:57
0

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)
Will Ness
  • 62,652
  • 8
  • 86
  • 167
0

I was doing the homework6 of CIS194 and find that you could write this way. Computing the first n elements requires only O(n) addition operations.

fibs2 :: [Integer]
fibs2 = [0, 1] ++ [fibs2 !! (n-1) + fibs2 !! (n-2) | n <- [2..]]
Lily Zhang
  • 11
  • 2