5

So i need to make a function described as

invFib :: Integer -> Maybe Integer

which takes an Integer and looks for it in the fibonacci sequence (as described by the function below)

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

and returns the index of the number example:

invFib 0 ~> Just 0

invFib 1 ~> Just 1 or Just 2

map invFib [54, 55, 56] ~> [Nothing,Just 10,Nothing]

invFib (fibs !! 99) ~> Just 99

I tried making a function that takes a list of integers and spits out the index, but it keeps failing. Any thoughts?

this is function i tried-

findNum :: [Integer] -> Integer -> Integer -> Integer
findNum x:xs y z = if x == y
                then z
                else findNum xs y (z+1)

Edit: the function freezes on numbers not in the fibonacci sequence, also only shows 1 value when 1 is entered

invFib :: Integer -> Maybe Integer
invFib n = if n < 0
        then Nothing
        else fmap fromIntegral (elemIndex n fibs)
dave4420
  • 46,404
  • 6
  • 118
  • 152
lopezrican304
  • 175
  • 1
  • 2
  • 7
  • 1
    You should post the code you tried if you want an explanation of the problem with it. – Pubby Apr 03 '13 at 01:44
  • 1
    `findNum` looks like a good start. You need to ask yourself this question: Given that `fibs` is an infinite list, how would you determine that, say, `54` is not in that list? – MtnViewMark Apr 03 '13 at 02:36

3 Answers3

8

So the key here is that fibs is infinite, but also monotonically increasing. Hence, once it exceeds the number being looked for, it can return Nothing:

findIndexInAscendingList :: (Ord a) => a -> [a] -> Maybe Integer
findIndexInAscendingList a xs = find 0 xs
  where
    find i [] = Nothing -- won't get used for fibs
    find i (x:xs) | a == x    = Just i
                  | a < x     = Nothing
                  | otherwise = find (i + 1) xs

invFib :: Integer -> Maybe Integer
invFib n = findIndexInAscendingList n fibs

And so:

$ ghci
GHCi, version 7.4.2: http://www.haskell.org/ghc/  :? for help
λ: :load Fib.hs 
[1 of 1] Compiling Main             ( Fib.hs, interpreted )
Ok, modules loaded: Main.
λ: map invFib [54,55,56]
[Nothing,Just 10,Nothing]

There are some other ways to do it too. Think about zip fibs [0..] and then you could use dropWhile to remove the portion less than n and test what's left.

MtnViewMark
  • 5,120
  • 2
  • 20
  • 29
  • FYI - `:set +s; let a = 10^25000; invFibMVMark a => "Nothing (0.38 secs, 13927928 bytes)"; invFibGroovy a => "Nothing (0.18 secs, 3633660 bytes)"` – גלעד ברקן Apr 04 '13 at 22:47
  • @groovy did you test the both of them fresh (i.e. including the time for generating the `fibs` sequence)? Was the file compiled before being loaded into GHCi? In my tests I seen MVMark's version run faster than yours, by 10%. Perhaps we can take it to say that the both versions are *comparable* in their performance. :) – Will Ness Apr 05 '13 at 08:59
  • @WillNess Here's what I did (in GHCi): `:load "bothMethods.hs"; :set +s; let a = 10^25000; invFibMVMark a; invFibGroovy a` I repeated the functions calls a few times and they seemed more or less consistent, fluctuating by an order of .01. – גלעד ברקן Apr 05 '13 at 11:02
  • @groovy yes, thanks. that means you loaded it interpreted, and your times are *not* from first runs, so `fibs` creation doesn't count. Good. That way I also get your code run faster (even by a wider margin; it depends on the GHC version I guess). I have my GHCi started with "-fobject-code" always, so compiling is done on load, and then there's no significant difference (10% was on first runs only; for next runs the timings were near 0 secs, for both versions). – Will Ness Apr 05 '13 at 11:23
7

Why not use a function like 'takeWhile' to return a section of the infinite 'fibs' list that you want to examine? With the finite list, you could apply a function like 'elemIndex' which, with a little type adjustment, could return what you are after.

elemIndex myInteger (takeWhile (<= myInteger) fibs)
גלעד ברקן
  • 23,602
  • 3
  • 25
  • 61
  • This feels wrong because in essence the work is being done twice: once by `takeWhile` and once by `findIndex`. – MtnViewMark Apr 03 '13 at 06:55
  • 1
    Only on a first glance. Every element is first `<=`’ed and then `==`ed. In your code, every element is first `==`’ed and everyone but the last is then `<`’ed. So the work is not done twice, and shows a nicely how modular Haskell can be. – Joachim Breitner Apr 03 '13 at 13:42
  • True - in both versions, as they sit, two comparison operations are applied to every element. In my code example there is only one explicit list traversal, whereas in this code there are two. That said, it is possible that the compiler will fuse `findIndex` and `takeWhile` leading to one list traversal. In my code, one could replace the guards with `case compare a x of` leading to one comparison per element. -- *All that being said,* my comment wasn't about efficiency, but about presentation of algorithm. It felt wrong to me because it specifies more work than need be done. – MtnViewMark Apr 04 '13 at 03:18
  • @MtnViewMark eh, I like mine better. Thanks for sharing your thoughts. – גלעד ברקן Apr 04 '13 at 04:34
  • It feels actually pretty right to me. First, you derive a finite sequence from an infinite one (assuming that finding an element that fails `(<= myInteger)` takes finite time), so that finding something will also take finite time. –  Apr 04 '13 at 09:10
  • @MtnViewMark two *simultaneous* traversals, because of lazyness. With each new cell produced by `takeWhile` immediately used by `elemIndex` and discarded by GC. Which will most probably get optimized into using just one reusable extra cell when compiled with optimizations. So that fusion isn't even necessary. – Will Ness Apr 04 '13 at 11:46
  • @MtnViewMark you function can actually be less efficient (unless optimized by the compiler) - it isn't forcing the index calculations, and uses `Integer` for index, whereas `findIndices` uses unboxed `Int`s. – Will Ness Apr 04 '13 at 11:52
1

If you've already computed fibs, then the answer is simple:

import Data.List

invFib :: Integer -> Maybe Integer
invFib n = fmap fromIntegral (elemIndex n fibs)

fibs :: [Integer]
fibs = 0:1:(zipWith (+) fibs (tail fibs))
Gabriella Gonzalez
  • 34,863
  • 3
  • 77
  • 135