Memoization techniques - ruby

While trying some memoization techniques I stumbled on this benchmark results which is against my expectations. Seems I am making some silly mistake, does someone see what I am doing wrong here (the benchmark gives similar results for memoized and non memoized code)?
require 'benchmark'
# -----------------------------------------
class FactorialClass
##sequence = [1]
def self.of( n )
##sequence[n] || n * of( n - 1 )
end
end
# -----------------------------------------
def factorial_with_memoization
sequence = [1]
lambda = Proc.new do |n|
sequence[n] || n * lambda.call( n - 1 )
end
end
f = factorial_with_memoization()
# -----------------------------------------
def factorial n
n == 0 ? 1 : n * factorial( n - 1 )
end
# -----------------------------------------
count = 1_000
n = 1000
without_memoization = Benchmark.measure do
count.times do
factorial(n)
end
end
with_memoization_lambda = Benchmark.measure do
count.times do
f.call(n)
end
end
with_memoization_class = Benchmark.measure do
count.times do
FactorialClass.of(n)
end
end
puts "Without memoization : #{ without_memoization }"
puts "With memoization using lambda : #{ with_memoization_lambda }"
puts "With memoization using class : #{ with_memoization_class }"
** The results are: **
Without memoization : 1.210000 0.100000 1.310000 ( 1.309675)
With memoization using lambda : 1.750000 0.100000 1.850000 ( 1.858737)
With memoization using class : 1.270000 0.090000 1.360000 ( 1.358375)

You never assign any memorized value to the cache. As #xlembouras said, you didn't memorize anything.
class FactorialClass
##sequence = [1]
def self.of( n )
# ##sequence[n] get evaluated to nil unless n is 0, always!
##sequence[n] || n * of( n - 1 )
end
end
You need to manually assign memorized value to the cache, after you finish the computation.
class FactorialClass
##sequence = [1]
def self.of( n )
##sequence[n] = (##sequence[n] || n * of( n - 1 ))
end
end
However, does memorization really works for your factorial computation? No.
f(n) = n * f(n-1) = n * ((n-1) * f(n-2)) = ... = n * ((n-1) * (... * (3 * f(2))))
All the recursion step calculates the factorial of a new value (from 2 to n), which hasn't been calculated before. The memorization won't get hit at any step.

Related

Factorial in Ruby

I'm trying to do in the Ruby program the summation of a factorial. and I can not solve. a clarification, when they run out points .... means that the logic is fine. if you get a F in one of the points, it means that the logic is wrong.
Write a program to calculate the sum of 1 + 1 / (2!) + 1 / (3!) + 1 / (4!) + .... + 1 / (n!) For a given n. Write the program in two ways: using While, For
def factorial(n)
#(n == 0) ? 1 : n * factorial(n - 1)
fact = 1
for i in 1..n
fact = fact * i
end
return fact
end
def sumatoriaWhile(n)
total = n
sumatoria = 0.0
while n > 1
total = total * (n - 1)
n = n - 1
sumatoria =sumatoria + total.to_f
end
return (1 + (1 / total.to_f)).round(2)
end
def sumatoriaFor(n)
fact = 1
sumatoria = 0.0
for i in 1..n
for j in 1..i
fact = fact * j
end
sumatoria = sumatoria + (1 / fact.to_f)
i = i + 1
end
return sumatoria.round(2)
end
#--- zona de test ----
def test_factorial
print validate(120, factorial(5))
print validate(5040, factorial(7))
print validate(362880, factorial(9))
end
def test_sumatoriaWhile
print validate(1.50, sumatoriaWhile(2))
print validate(1.83, sumatoriaWhile(3))
end
def test_sumatoriaFor
print validate(1.50, sumatoriaFor(2))
print validate(1.83, sumatoriaFor(3))
end
def validate (expected, value)
expected == value ? "." : "F"
end
def test
puts "Test program"
puts "---------------------------"
test_factorial
test_sumatoriaWhile
test_sumatoriaFor
puts " "
end
test
My friend, Thank you for your prompt response. First of all, I appreciate the help given. I am learning ruby programming and want to learn more as you and the other people. if indeed the answer is wrong. I have modified the answer. and also need to know what the function of While. and I hereby amended program again. and now I get an F on the part of WHILE.
def factorial(n)
fact = 1
for i in 1..n
fact = fact * i
end
return fact
end
def sumatoriaWhile(n)
total = n
sumatoria = 0.0
while n < 1
total = total * (n - 1)
sumatoria = sumatoria + (1.0 / total.to_f)
n = n - 1
end
return sumatoria.round(2)
end
def sumatoriaFor(n)
fact = 1
sumatoria = 0.0
for i in 1..n
fact = fact * i
sumatoria = sumatoria + (1.0 / fact.to_f)
end
return sumatoria.round(2)
end
#--- zona de test ----
def test_factorial
print validate(120, factorial(5))
print validate(5040, factorial(7))
print validate(362880, factorial(9))
end
def test_sumatoriaWhile
print validate(1.50, sumatoriaWhile(2))
print validate(1.67, sumatoriaWhile(3))
end
def test_sumatoriaFor
print validate(1.50, sumatoriaFor(2))
print validate(1.67, sumatoriaFor(3))
end
def validate (expected, value)
expected == value ? "." : "F"
end
def test
puts "Test de prueba del programa"
puts "---------------------------"
test_factorial
test_sumatoriaWhile
test_sumatoriaFor
puts " "
end
test
I have a hard time figuring out what you're doing in the summation function. Here's a straightforward function:
def sumatoriaFor(n)
return 0 if n <= 0
factorial = 1
sum = 0.0
for i in 1..n
factorial *= i
sum += 1.0 / factorial.to_f
end
return sum.round(2)
end
def sumatoriaWhile(n)
return 0 if n <= 0
i = 1
factorial = 1
sumatoria = 0.0
while i <= n
factorial *= i
sumatoria += (1.0 / factorial.to_f)
i = i + 1
end
return sumatoria.round(2)
end
The while look is straight forward too now. Also your validation is wrong:
1 + 1/2 + 1/6 ~= 1 + 0.5 + 0.17 = 1.67

Ruby compute cumulative sum from endpoints recursively?

Given two numbers, say (14, 18), the problem is to find the sum of all the numbers in this range, 14, 15, 16, 17, 18 recursively. Now, I have done this using loops but I have trouble doing this recursively.
Here is my recursive solution:
def sum_cumulative_recursive(a,b)
total = 0
#base case is a == b, the stopping condition
if a - b == 0
puts "sum is: "
return total + a
end
if b - a == 0
puts "sum is: "
return total + b
end
#case 1: a > b, start from b, and increment recursively
if a > b
until b > a
puts "case 1"
total = b + sum_cumulative_recursive(a, b+1)
return total
end
end
#case 2: a < b, start from a, and increment recursively
if a < b
until a > b
puts "case 2"
total = a + sum_cumulative_recursive(a+1, b)
return total
end
end
end
Here are some sample test cases:
puts first.sum_cumulative_recursive(4, 2)
puts first.sum_cumulative_recursive(14, 18)
puts first.sum_cumulative_recursive(-2,-2)
My solution works for cases where a > b, and a < b, but it doesn't work for a == b.
How can I fix this code so that it works?
Thank you for your time.
def sum_cumulative_recursive(a,b)
return a if a == b
a, b = [a,b].sort
a + sum_cumulative_recursive(a + 1, b)
end
EDIT
Here is the most efficient solution I could see from some informal benchmarks:
def sum_cumulative_recursive(a,b)
return a if a == b
a, b = b, a if a > b
a + sum_cumulative_recursive(a + 1, b)
end
Using:
Benchmark.measure { sum_cumulative_recursive(14,139) }
Benchmark for my initial response: 0.005733
Benchmark for #Ajedi32's response: 0.000371
Benchmark for my new response: 0.000115
I was also surprised to see that in some cases, the recursive solution approaches or exceeds the efficiency of the more natural inject solution:
Benchmark.measure { 10.times { (1000..5000).inject(:+) } }
# => 0.010000 0.000000 0.010000 ( 0.027827)
Benchmark.measure { 10.times { sum_cumulative_recursive(1000,5000) } }
# => 0.010000 0.010000 0.020000 ( 0.019441)
Though you run into stack level too deep errors if you take it too far...
I'd do it like this:
def sum_cumulative_recursive(a, b)
a, b = a.to_i, b.to_i # Only works with ints
return sum_cumulative_recursive(b, a) if a > b
return a if a == b
return a + sum_cumulative_recursive(a+1, b)
end
Here's one way of doing it. I assume this is just an exercise, as the sum of the elements of a range r is of course just (r.first+r.last)*(f.last-r.first+1)/2.
def sum_range(range)
return nil if range.last < range.first
case range.size
when 1 then range.first
when 2 then range.first + range.last
else
range.first + range.last + sum_range(range.first+1..range.last-1)
end
end
sum_range(14..18) #=> 80
sum_range(14..14) #=> 14
sum_range(14..140) #=> 9779
sum_range(14..139) #=> 9639
Another solution would be to have a front-end invocation that fixes out-of-order arguments, then a private recursive back-end which does the actual work. I find this is useful to avoid repeated checks of arguments once you've established they're clean.
def sum_cumulative_recursive(a, b)
a, b = b, a if b < a
_worker_bee_(a, b)
end
private
def _worker_bee_(a, b)
a < b ? (a + _worker_bee_(a+1,b-1) + b) : a == b ? a : 0
end
This variant would cut the stack requirement in half by summing from both ends.
If you don't like that approach and/or you really want to trim the stack size:
def sum_cumulative_recursive(a, b)
if a < b
mid = (a + b) / 2
sum_cumulative_recursive(a, mid) + sum_cumulative_recursive(mid+1, b)
elsif a == b
a
else
sum_cumulative_recursive(b, a)
end
end
This should keep the stack size to O(log |b-a|).

How do I calculate the factorial of a number in Ruby?

Alright, so I asked an earlier question on my syntax error. I got rid of the errors, but the program doesn't do what it was intended to do. My math is wrong and doesn't find the number of trailing zeros. Here is my code:
num = " "
a = 0
sumOfFact = 1
def factorial
num = gets.to_i
a = num
(1..num).each do |a|
if a != 1
sumOfFact *= a
a -= 1
else
break
end
end
end
for c in 1..sumOfFact
if sumOfFact / c == 10
zeros += 1
end
end
factorial()
puts sumOfFact
puts zeros
Well, first, you should do the gets outside your method. Your method should accept a param. Second, why do you need the condition?
You want the multiplication from 1 to n to get the factorial. You should get started with this:
def factorial(n)
total = 1
(1..n).each do |n|
total *= n
end
total
end
puts factorial(gets.to_i)
Next is factorial with inject in case you want to learn new syntax :-)
def factorial(n)
n == 0? 1 : (1..n).inject(1) { |total, i| total*= i; total }
end
puts factorial(gets.to_i)
As #pjs commented below, here's a beautiful way of doing factorial!
def factorial(n)
n == 0? 1 : (1..n).inject(:*)
end
And, a final enhancement:
def factorial(n)
(1..n).inject(1, :*)
end
Supposing that n is a non-negative integer number, you can define a method to calculate the factorial:
def factorial(n)
tot = 1
(1..n).each do |n|
tot *= x
end
tot
end
Examples of its usage:
puts factorial(0) # 1
puts factorial(1) # 1
puts factorial(2) # 2
puts factorial(3) # 6
puts factorial(4) # 24
puts factorial(5) # 120
If you wan't to read the user input, call it like this:
puts 'Type the non-negative integer:'
n = gets.to_i
puts factorial(n)
class Factorial
attr_reader :num
def initialize(num)
#num = num
end
def find_factorial
(1..num).inject(:*) || 1
end
end
number = Factorial.new(8).find_factorial
puts number
Or you could just simply write:
(1..num).inject(:*) || 1
Try this too. Hope this helps anyone having the same problem in some way.
Method for finding the factorial of any number:
def factorial(number)
for i in 1...number do
number *= i
end
number
end
puts factorial(5)

Which Ruby statement is more efficient?

I have a hash table:
hash = Hash.new(0)
hash[:key] = hash[:key] + 1 # Line 1
hash[:key] += 1 # Line 2
Line 1 and Line 2 do the same thing. Looks like line 1 needs to query hash by key two times while line 2 only once. Is that true? Or they are actually same?
I created a ruby script to benchmark it
require 'benchmark'
def my_case1()
#hash[:key] = #hash[:key] + 1
end
def my_case2()
#hash[:key] += 1
end
n = 10000000
Benchmark.bm do |test|
test.report("case 1") {
#hash = Hash.new(1)
#hash[:key] = 0
n.times do; my_case1(); end
}
test.report("case 2") {
#hash = Hash.new(1)
#hash[:key] = 0
n.times do; my_case2(); end
}
end
Here is the result
user system total real
case 1 3.620000 0.080000 3.700000 ( 4.253319)
case 2 3.560000 0.080000 3.640000 ( 4.178699)
It looks hash[:key] += 1 is slightly better.
#sza beat me to it :)
Here is my example irb session:
> require 'benchmark'
=> true
> n = 10000000
=> 10000000
> Benchmark.bm do |x|
> hash = Hash.new(0)
> x.report("Case 1:") { n.times do; hash[:key] = hash[:key] + 1; end }
> hash = Hash.new(0)
> x.report("Case 2:") { n.times do; hash[:key] += 1; end }
> end
user system total real
Case 1: 1.070000 0.000000 1.070000 ( 1.071366)
Case 2: 1.040000 0.000000 1.040000 ( 1.043644)
The Ruby Language Specification spells out the algorithm for evaluating abbreviated indexing assignment expressions quite clearly. It is something like this:
primary_expression[indexing_argument_list] ω= expression
# ω can be any operator, in this example, it is +
is (roughly) evaluated like
o = primary_expression
*l = indexing_argument_list
v = o.[](*l)
w = expression
l << (v ω w)
o.[]=(*l)
In particular, you can see that both the getter and the setter are called exactly once.
You can also see that by looking at the informal desugaring:
hash[:key] += 1
# is syntactic sugar for
hash[:key] = hash[:key] + 1
# which is syntactic sugar for
hash.[]=(:key, hash.[](:key).+(1))
Again, you see that both the setter and the getter are called exactly once.
second one is the customary way of doing it. It is more efficient.

Ruby - get bit range from variable

I have a variable and want to take a range of bits from that variable. I want the CLEANEST way to do this.
If x = 19767 and I want bit3 - bit8 (starting from the right):
100110100110111 is 19767 in binary.
I want the part in parenthesis 100110(100110)111 so the answer is 38.
What is the simplest/cleanest/most-elegant way to implement the following function with Ruby?
bit_range(orig_num, first_bit, last_bit)
PS. Bonus points for answers that are computationally less intensive.
19767.to_s(2)[-9..-4].to_i(2)
or
19767 >> 3 & 0x3f
Update:
Soup-to-nuts (why do people say that, anyway?) ...
class Fixnum
def bit_range low, high
len = high - low + 1
self >> low & ~(-1 >> len << len)
end
end
p 19767.bit_range(3, 8)
orig_num.to_s(2)[(-last_bit-1)..(-first_bit-1)].to_i(2)
Here is how this could be done using pure number operations:
class Fixnum
def slice(range_or_start, length = nil)
if length
start = range_or_start
else
range = range_or_start
start = range.begin
length = range.count
end
mask = 2 ** length - 1
self >> start & mask
end
end
def p n
puts "0b#{n.to_s(2)}"; n
end
p 0b100110100110111.slice(3..8) # 0b100110
p 0b100110100110111.slice(3, 6) # 0b100110
Just to show the speeds of the suggested answers:
require 'benchmark'
ORIG_NUMBER = 19767
def f(x,i,j)
b = x.to_s(2)
n = b.size
b[(n-j-1)...(n-i)].to_i(2)
end
class Fixnum
def bit_range low, high
len = high - low + 1
self >> low & ~(-1 >> len << len)
end
def slice(range_or_start, length = nil)
if length
start = range_or_start
else
range = range_or_start
start = range.begin
length = range.count
end
mask = 2 ** length - 1
self >> start & mask
end
end
def p n
puts "0b#{n.to_s(2)}"; n
end
n = 1_000_000
puts "Using #{ n } loops in Ruby #{ RUBY_VERSION }."
Benchmark.bm(21) do |b|
b.report('texasbruce') { n.times { ORIG_NUMBER.to_s(2)[(-8 - 1)..(-3 - 1)].to_i(2) } }
b.report('DigitalRoss string') { n.times { ORIG_NUMBER.to_s(2)[-9..-4].to_i(2) } }
b.report('DigitalRoss binary') { n.times { ORIG_NUMBER >> 3 & 0x3f } }
b.report('DigitalRoss bit_range') { n.times { 19767.bit_range(3, 8) } }
b.report('Philip') { n.times { f(ORIG_NUMBER, 3, 8) } }
b.report('Semyon Perepelitsa') { n.times { ORIG_NUMBER.slice(3..8) } }
end
And the output:
Using 1000000 loops in Ruby 1.9.3.
user system total real
texasbruce 1.240000 0.010000 1.250000 ( 1.243709)
DigitalRoss string 1.000000 0.000000 1.000000 ( 1.006843)
DigitalRoss binary 0.260000 0.000000 0.260000 ( 0.262319)
DigitalRoss bit_range 0.840000 0.000000 0.840000 ( 0.858603)
Philip 1.520000 0.000000 1.520000 ( 1.543751)
Semyon Perepelitsa 1.150000 0.010000 1.160000 ( 1.155422)
That's on my old MacBook Pro. Your mileage might vary.
Makes sense to define a function for that:
def f(x,i,j)
b = x.to_s(2)
n = b.size
b[(n-j-1)...(n-i)].to_i(2)
end
puts f(19767, 3, 8) # => 38
Expanding on the idea from DigitalRoss - instead of taking two arguments, you can pass a range:
class Fixnum
def bit_range range
len = range.last - range.first + 1
self >> range.first & ~(-1 >> len << len)
end
end
19767.bit_range 3..8

Resources