Different memoization techniques in Ruby
If you are a ruby programmer then you might have come across the hash block memoization pattern. For a simple example I present you the memoized version of the Fibonacci sequence:
fib_hash = Hash.new do |h,i|
h[i] = 开发者_如何学编程h[i-1] + h[i-2]
end
# establish the base cases
fib_hash[1] = 1; fib_hash[2] = 1
Of course this is not the only way to create a memoized version of the Fibonacci sequence. You could also do the following:
@cache = {}; @cache[1] = 1; @cache[2] = 1
def memo_fib(n)
@cache[n] ||= (memo_fib(n-1) + memo_fib(n-2))
end
Hopefully you see how the hash block memoization pattern maps to the second version which is much more common in many other languages. What I would like to know is if there is any difference between the two versions? I can't shake the feeling that the hash block version is more efficient but I can't really justify why.
A benchmark in MRI 1.8.7 yields:
- Hash block: 0.44 seconds
- Non hash block: 0.41 seconds
And in MRI 1.9.0:
- Hash block: 0.24 seconds
- Non hash block: 0.28 seconds
The benchmark is 100 iterations of computing the Fibonacci numbers from 1 through 1000, resetting the hash or cache at the start of each iteration.
Here's the benchmark for the hash block:
def reset_fib_hash
@fib_hash = Hash.new do |h,i|
h[i] = h[i-1] + h[i-2]
end
# establish the base cases
@fib_hash[1] = 1; @fib_hash[2] = 1
end
start_time = Time.now
100.times do
reset_fib_hash
(1..1000).each do |i|
@fib_hash[i]
end
end
p Time.now - start_time
Here's the benchmark for the non hash block:
def reset_fib_hash
@cache = {}; @cache[1] = 1; @cache[2] = 1
end
def memo_fib(n)
@cache[n] ||= (memo_fib(n-1) + memo_fib(n-2))
end
start_time = Time.now
100.times do
reset_fib_hash
(1..1000).each do |i|
memo_fib(i)
end
end
p Time.now - start_time
精彩评论