Related
This week is my first time doing recursion. One of the problems I was able to solve was Fibonacci's sequence to the nth number; it wasn't hard after messing with it for 5 minutes.
However, I am having trouble understanding why this works with the current return statement.
return array if num == 2
If I push to array, it doesn't work, if I make a new variable sequence and push to that, it returns the correct answer. I am cool with that, but my base case says return array, not sequence. I initially pushed the sequence to the array, the result was not fibs sequence. I only solved the problem when I tried seeing what would happen if I pushed to the sequence array.
Instead of just making it work I was hoping someone could explain what was happening under the hood, what the stacks might be and how the problem works.
I understand recursion to an extent and somehow intuitively can make it work by assuming things, but I feel funny not actually knowing all the whys behind it.
def fib_seq(num)
return [0] if num == 1
return [] if num == 0
array = [0, 1]
return array if num <= 2
seq = fib_seq(num - 1)
seq << seq[-2] + seq[-1]
end
The code can be simplified a bit by removing the temporary array variable. It's a distraction. It also only applies when num == 2; num < 2 will be handled by the other base cases. num < 0 is illegal and should be handled by an error check.
I've also added in an explicit return. Explicit returns make it very obvious what's being returned and that helps understand recursion. In this case it's seq. ("Explicit returns are evil!" all the Ruby style people cry. Tough cookies. Good style isn't an absolute.)
def fib_seq(num)
# Error check
if num < 0 then
raise ArgumentError, "The number must be a positive integer"
end
# Terminating base cases
return [] if num == 0
return [0] if num == 1
return [0,1] if num == 2
# Recursion
seq = fib_seq(num - 1)
# The recursive function
seq << seq[-2] + seq[-1]
return seq
end
Now it's a bit clearer that return [0,1] if num == 2 is one of three base cases for the recursion. These are the terminating conditions which stops the recursion. But processing doesn't end there. The result isn't [0,1] because after that first return the stack has to unwind.
Let's walk through fib_seq(4).
fib_seq(4) calls fib_seq(3)
fib_seq(3) calls fib_seq(2)
fib_seq(2) returns `[0,1]`
We've reached the base case, now we need to unwind that stack of calls.
The call to fib_seq(3) picks up where it left off. seq returned from fib_seq(2) is [0,1]. It adds seq[-2] + seq[-1] onto the end and returns [0,1,1].
fib_seq(4) picks up where it left off. seq returned from fib_seq(3) is [0,1,1]. It adds seq[-2] + seq[-1] to the end and returns [0,1,1,2].
The stack is unwound, so we get back [0,1,1,2].
As you can see, the actual calculation happens backwards. f(n) = f(n-1) + f(n-2) and f(2) = [0,1]. It recurses down to f(2), the base case, then unwinds back up doing f(3) using the result of f(2), and f(4) using the result of f(3) and so on.
Recursive functions need to have an exit condition to prevent them from running forever. The main part of your recursive method is the following:
seq = fib_seq(num - 1)
seq << seq[-2] + seq[-1]
In Ruby, the last expression of a method is considered to be the return value of that method, so the lines above are equivalent to:
seq = fib_seq(num - 1)
seq << seq[-2] + seq[-1]
return seq
Let's run down what would happen if the method only contained these two lines, with num = 4:
call fib_seq(4)
call fib_seq(3)
call fib_seq(2)
call fib_seq(1)
call fib_seq(0)
call fib_seq(-1)
...
Obviously this results in an infinite loop, since we have no exit condition. We always call fib_seq again on the first line, so the code has no chance of ever reaching the return statement at the end. To fix the problem, let's add in these two lines at the beginning:
array = [0, 1]
return array if num <= 2
These can be simplified down to just:
return [0, 1] if num <= 2
Now let's see what happens when we call the method with num = 4:
call fib_seq(4)
4 > 2, exit condition not triggered, calling fib_seq(n - 1)
call fib_seq(3)
3 > 2, exit condition not triggered, calling fib_seq(n - 1)
call fib_seq(2)
2 == 2, exit condition triggered, returning [0, 1]!
fib_seq(2) returned with seq = [0, 1]
add 0 + 1 together, push new value to seq
seq is now [0, 1, 1]
return seq
fib_seq(3) returned with seq = [0, 1, 1]
add 1 + 1 together, push new value to seq
seq is now [0, 1, 1, 2]
return seq
FINAL RESULT: [0, 1, 1, 2]
So it looks like this method is working for values of num that are >= 2:
def fib_seq(num)
return [0, 1] if num <= 2
seq = fib_seq(num - 1)
seq << seq[-2] + seq[-1]
end
There is one bug left: num = 0 and num = 1 both return [0, 1]. Let's fix that:
def fib_seq(num)
return [] if num == 0
return [0] if num == 1
return [0, 1] if num == 2
seq = fib_seq(num - 1)
seq << seq[-2] + seq[-1]
end
Clean it up a little:
def fib_seq(num)
return [0, 1].first(num) if num <= 2
seq = fib_seq(num - 1)
seq << seq[-2] + seq[-1]
end
I always find it confusing when people mix imperative style mutations with functional style recursion – if you're going to do all reassignment and manual array seeking, why bother with using recursion as the looping mechanism? just use a loop.
That's not to say this program can't be expressed in a more functional way, tho. Here, we separate concerns of computing fibonacci numbers and generating a sequence – the result is an extremely easy-to-understand program
def fib n
def aux m, a, b
m == 0 ? a : aux(m - 1, b, a + b)
end
aux n, 0, 1
end
def fib_seq n
(0..n).map &method(:fib)
end
fib_seq 10
#=> [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]
And another way that's a bit more efficient for generating the sequence specifically – Below, I define an axuiliary function aux that utilizes 4 state variables to generate the sequence in a relatively straightforward way.
Note the difference with the input 10 - this one is closer to your proposed function where 0 returns [] despite the 0th fibonacci number is actually 0
def fib_seq n
def aux acc, m, a, b
m == 0 ? acc << a : aux(acc << a, m - 1, b, a + b)
end
case n
when 0; []
when 1; [0]
when 2; [0,1]
else; aux [0,1], n - 3, 1, 2
end
end
fib_seq 10
# => [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
I'm currently working on small ruby projects from project Euler site. I was given a task to sum even fibonacci numbers that are less than 4 millions. Unfortunately there is a small bug in my code, because when I change the limit e.i. to 100, it prints 188 instead of 44. Surprisingly this program gives the right answer but i don't really know in what way my code is wrong.
a=[]; a[0]=1; a[1]=1;
i = 1
while a[-1] < 608
a[i+1]=(a[i] + a[i-1])
i +=1
end
x = 0
a.each do |num|
if num % 2 == 0
x += num
end
end
print "The sum of even Fibonacci number is: #{x}"
The problem comes from the second iteration. You are stopping the generation of Fibonacci numbers when one of the numbers cross the limit (ie when the last number is > 100).
It turns out that after the generation step, the array is [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144], this explains your wrong result 188 = 144+44.
So, your code works only when the last element generated is odd, which is the case in Euler's problem test. In order to correct that, change your second iteration from a.each do ... end to a[0...-1].each do ... end In order to iterate through the array except the last element.
BTW I would recommend you not to use an array here.
You are just wasting memory and ruby is losing time on extending it (this can be solved via Array.new(ARRAY_SIZE)).
Since you don't actually need a fibbonaci sequence you can just have something like this:
LIMIT = 4_000_000
a = 1
b = 1
next_number = a + b
sum = 0
while next_number < LIMIT
sum += next_number if next_number.even?
a = b
b = next_number
next_number = a + b # or next_number += a
end
UPD. Oh my god I don't know why this question appeared in my feed. Sorry for necroposting:)
I have a Ruby array that defines a set of integer thresholds
thresholds = [under_threshold_1, under_threshold_2, ..., under_threshold_n, over_threshold]
I want to map any integer to a value corresponding to the threshold number. Basically
if threshold_a < number < threshold_b
return threshold_a
end
Is there a cool way to do that in Ruby ? I Need to handle the "edge" cases < threshold_1 and > threshold_over. I could only come up with a (ugly but working) set of if statements or looping on the array.
I am actually free to modelise this how I want (I can change the array to something else if more convenient)
I was thinking there is maybe a cool way to splat the thresholds in a case/when clause
case number
when 0..threshold_1 then 0
when threshold_i..threshold_i+1 then i
else n
end
# example
thresholds = [ 4, 8, 10 ,12 ]
quantify(1) = 0
quantify(4) = 1
quantify(11) = 3
quantify(50) = 4
How about this?
thresholds = [ 4, 8, 10, 12 ]
def which_threshold(thresholds, n)
thresholds.find_index {|t| n < t } || thresholds.size
end
p which_threshold(thresholds, 1) # => 0
p which_threshold(thresholds, 4) # => 1
p which_threshold(thresholds, 11) # => 3
p which_threshold(thresholds, 50) # => 4
I think this is what you want:
Thresholds = [4, 8, 10, 12]
def quantify(n)
Thresholds.count { |t| n >= t }
end
This quantification of n you want happens to be the number of thresholds that n is greater than or equal to, and it's easy to compute that using Enumerable#count.
I try to implement shell sort by ruby.
def shell_sort(list)
d = list.length
return -1 if d == 0
(0...list.length).each do |i|
d = d / 2
puts "d:#{d}"
(0...(list.length-d)).each do |j|
if list[j] >= list[j+d]
list[j], list[j+d] = list[j+d], list[j]
end
end
puts list.inspect
break if d == 1
end
list
end
puts shell_sort([10,9,8,7,6,5,4,3,2,1]).inspect
but the result is incorrect.
=>[2, 1, 3, 4, 5, 7, 6, 8, 9, 10]
I don't know where going wrong, hope someone can help me. Thanks in advance!
I referenced Shell Sort in here : Shell Sort - Wikepedia, and from that I have understood your algorithm is wrong. Iteration of gap sequence is alright, I mean you iterate only upto d/2 == 1.
But for a gap, let's say 2, you simply iterate from 0 to list.length-2 and swap every j and j+2 elements if list[j] is greater than list[j+2]. That isn't even a proper insertion sort, and Shell Sort requires Insertion sorts on gaps. Also Shell Sort requires that after you do an x gap sort, every xth element, starting from anywhere will be sorted (see the example run on the link and you can verify yourself).
A case where it can wrong in a 2 gap sort pass :
list = 5,4,3,2,1
j = 0 passed :
list = 3,4,5,2,1
j = 1 passed :
list = 3,2,5,4,1
j = 2 passed
list = 3,2,1,4,5
After it completes, you can see that every 2nd element starting from 0 isn't in a sorted order. I suggest that you learn Insertion Sort first, then understand where and how it is used in Shell Sort, and try again, if you want to do it by yourself.
Anyway, I have written one (save it for later if you want) taking your method as a base, with a lot of comments. Hope you get the idea through this. Also tried to make the outputs clarify the how the algorithm works.
def shell_sort(list)
d = list.length
return -1 if d == 0
# You select and iterate over your gap sequence here.
until d/2 == 0 do
d = d / 2
# Now you pick up an index i, and make sure every dth element,
# starting from i is sorted.
# i = 0
# while i < list.length do
0.step(list.length) do |i|
# Okay we picked up index i. Now it's just plain insertion sort.
# Only difference is that we take elements with constant gap,
# rather than taking them up serially.
# igap = i + d
# while igap < list.length do
(i+d).step(list.length-1, d) do |igap|
# Just like insertion sort, we take up the last most value.
# So that we can shift values greater than list[igap] to its side,
# and assign it to a proper position we find for it later.
temp = list[igap]
j = igap
while j >= i do
break if list[j] >= list[j - d]
list[j] = list[j-d]
j -= d
end
# Okay this is where it belongs.
list[j] = temp
#igap += d
end
# i += 1
end
puts "#{d} sort done, the list now : "
puts list.inspect
end
list
end
list = [10,9,8,7,6,5,4,3,2,1]
puts "List before sort : "
puts list.inspect
shell_sort(list)
puts "Sorted list : "
puts list.inspect
I think your algorithm needs a little tweaking.
The reason it fails is simply because on the last run (when d == 1) the smallest element (1) isn't near enough the first element to swap it in in one go.
The easiest way to make it work is to "restart" your inner loop whenever elements switch places. So, a little bit rough solution would be something like
(0...(list.length-d)).each do |j|
if list[j] >= list[j+d]
list[j], list[j+d] = list[j+d], list[j]
d *= 2
break
end
end
This solution is of course far from optimal, but should achieve required results with as little code as possible.
You should just do a last run on array. To simplify your code I extracted exchange part into standalone fucntion so you could see now where you should do this:
def exchange e, list
(0...(list.length-e)).each do |j|
if list[j] >= list[j+e]
list[j], list[j+e] = list[j+e], list[j]
end
end
end
def shell_sort(list)
d = list.length
return -1 if d == 0
(0...list.length).each do |i|
d = d / 2
puts "d:#{d}"
exchange(d, list)
puts list.inspect
if d == 1
exchange(d, list)
break
end
end
list
end
arr = [10,9,8,7,6,5,4,3,2,1]
p shell_sort(arr)
Result:
#> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Given an array
[50,30,0,0,10,0,30,60,0]
I need to replace the zeroes with calculated values to create a 'curve', so for example, between 10 and 30, the zero could be replaced with 20.
I keep thinking there must be a cool ruby way of doing this, but I cant find one. Can anyone help? The solution needs to take into account multiple adjacent zeroes, and zeroes at the start and end of the range.
Anyone any ideas?
The term you seem to be unaware of is interpolation. The Wikipedia article is a good place to start - exactly what algoithm is best suited to you depends on the exact context of your problem so we can't give you the one true answer here.
a =[50,30,0,0,10,0,30,60,0]
a.each_index{|i| a[i] = a[i-1] - ((a[i-2] - a[i-1])/2).to_i if a[i] == 0 && i > 1 }
puts a.inspect # [50, 30, 20, 15, 10, 8, 30, 60, 75]
I can't work out why the last number might be 80 in your spec however? Plus it doesn't work for the first two items in the array.
If there could not be consecutive zeroes, this unintelligible one-liner would do the trick (list is the given list of numbers):
[0, *list, 0].each_cons(3).map { |p, x, n| x == 0 ? (p + n)/2 : x }
Ruby 1.9 only, I think.
def find_consecutive_values( array, value=nil )
raise "Need a value or block to find" unless value || block_given?
start = last = nil
ranges = []
indices = array.each_with_index do |o,i|
if ((block_given? && yield(o)) || o==value)
start = i unless start
last = i
else
ranges << (start..last) if start && last
start = last = nil
end
end
ranges << (start..last) if start && last
ranges
end
def interpolate_zeros( array, round=false )
result = array.dup
find_consecutive_values( array, 0 ).each do |range|
next unless range.first>0 && range.last<(array.length-1)
before = result[range.first - 1]
after = result[range.last + 1]
diff = after - before
size = (range.last - range.first + 2).to_f
range.each_with_index do |i,idx|
value = before + diff * (idx+1)/size
value = value.round if round
result[i] = value
end
end
result
end
p interpolate_zeros( [0,50,30,0,0,10,0,30,60,0], true )
#=> [0, 50, 30, 23, 17, 10, 20, 30, 60, 0]
Just stumbled across this question. There is a ruby gem "interpolator", which just does what you want and probably tons more:
http://interpolator.rubyforge.org.
Here is a short introduction:
http://fabianosoriani.wordpress.com/2010/02/23/ruby-interpolation-with-gem-interpolator/