Binet’s formula and Haskell
This is an extended version of my entry in the Lockdown Mathoff at the Aperiodical
Binet’s formula 1 is a lovely way to generate the
Haskell and computation
The main reason I’m writing about this is this post. Haskell is something I’ve been meaning to play with for a while - it has a reputation of having a fearsome learning curve, and of being a very mathematical language. I’m not sure what that means, precisely, but it interests me. Implementing Binet’s formula struck me as a nice First Proper Problem to solve in Haskell.
I didn’t want to do it the same way as Gabriel did - I wouldn’t know a monoid from a monorail - but I did take heed of his warning not to do it with floating point arithmetic. Instead, I figured it would be nice to work with elements of
If I rewrite Binet’s formula as
Multiplying 2-tuples
I figured that I could use 2-tuples for this - I can represent
-- Tuple multiplication: (a + b√5)(c + d√5) = (ac + 5bd) + (ad + bc)√5 fmul :: (Integer, Integer) -> (Integer, Integer) -> (Integer, Integer) fmul (a,b) (c,d) = (a*c + 5*b*d, a*d + b*c)
The first line is a comment explaining what I’m doing, like I did above.
The second is a type description, which I’m (generally) a bit hazy on, details-wise; here, it says fmul takes a 2-tuple of Integers, a second 2-tuple of Integers, and returns a third 2-tuple of Integers. (Haskell Integers go as big as you need them to, as opposed to Ints, which are limited to - I think - 64 bits.)
The third line tells the compiler how to compute the function: if you fmul two 2-tuples together, you get a third 2-tuple defined exactly as I set out before.
In principle, I imagine I could figure out the
Working out powers
So, it’ll be helpful if I tell Haskell how to square one of my 2-tuples:
-- Tuple squaring (for efficiency) fsq :: (Integer, Integer) -> (Integer, Integer) fsq (a,b) = fmul (a,b) (a,b)
This time, fsq is a function that takes a single 2-tuple and returns a single 2-tuple, and it’s defined in the way you’d expect: you use fmul to multiply the tuple by itself.
Now to implement the recursive powers:
-- Powers of the tuples - divide and conquer fpow :: Integer -> (Integer, Integer) fpow 0 = (1,0) fpow 1 = (1,1) fpow n \| mod n 2 == 0 = fsq $ fpow $ div n 2 \| otherwise = fmul (1, 1) (fsq $ fpow $ div (n-1) 2)
fpow is going to work out the
This time, the definition is a bit more complicated: I need to explain what to return in several different cases. The first two are straightforward base cases: (1,1)
The grunt work is in the line that starts fpow n. It has a guard next to it, the | symbol, which tells Haskell to do different things under different conditions. Here, mod 2 n == 0 asks “is the number even?” - in which case, I want to square fpow applied to
And otherwise means… well, otherwise. Otherwise, I need to multiply (1,1) by the appropriate square - again, the dollars are there to make it work.
Getting the Fibonacci numbers
So far so good. However, I need to work out (1,1)
-- fib n is the nth Fibonacci number fib :: Integer -> Integer fib n = div (snd $ fpow n) (2 ^ (n-1))
And it’s quick! On my machine, working out the hundred-millionth Fibonacci number (which begins 4737103… and ends …0546875, and is about 20 million digits long) takes a handful of seconds to work out (and considerably longer to print).
One last ‘rather neatly’
Rather neatly, the first element of the 2-tuple, divided by
Footnotes:
1. As ordained by Stigler’s law (which is attributed to Merton), Binet’s formula was known at least a century before Binet wrote about it
2. Ridiculously pedantic, you say? I refer the reader to the comment about Haskell being a very mathematical language.