6 comments
I wonder whether the array version or a hash version would be faster/less resource intensive. Would this code be better or worse?
1 2 3 4 5 6 7 |
@cache = {0 => 0, 1 => 1} def fib(n) return @cache[n] if @cache[n] @cache[n] = fib(n-1) + fib(n-2) end |
By the way, I like that your way of setting this up just adds the basic version to the variable, so that it is unnecessary to write “return n if n < 2” at the top of the function.
I would say using a hash here doesn’t provide any benefits since we aren’t scanning the array, we are accessing the index directly. That’s a constant time operation, so it doesn’t get much better than that.
I like your solution! It’s very similar to the “hash” version:
1 2 3 4 5 6 |
fib = Hash.new { |cache, n| cache[n] = fib[n - 1] + fib[n - 2] } # prefill fib[0] = 0 fib[1] = 1 fib[23] # => 28657 |
This will run a lot faster and it will be able to calculate much bigger Fibonacci numbers.
Unfortunately, this is not the case, because of a key limitation in Ruby – the lack of tail call optimization. On my vanilla 2.1.5 build of Ruby, the highest number I can run your recursive, memoized solution of fibs
with is 7685 before I get a SystemStackError: stack level too deep
Thanks for pointing that out, I was aware of the issue but wasn’t sure how to put it into words. I ran some tests and it seems like the stack size is always the same on every machine. On a Linux VM the ulimit
parameter for max stack size didn’t seem to have an effect.
I used this code to find the maximum number that can be calculated recursively:
(0...99999).each { |n| fib(n); p "Calculating fib #{n}"; @cache = [0,1] }
You can actually turn TCO on in ruby, if you are so inclined. That would make it possible to write the recursive example w/out eating up stack: http://nithinbekal.com/posts/ruby-tco/