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]
Related
I'm a beginner in Ruby and I don't understand what this code is doing, could you explain it to me, please?
def a(n)
s = 0
for i in 0..n-1
s += i
end
s
end
def defines a method. Methods can be used to run the same code on different values. For example, lets say you wanted to get the square of a number:
def square(n)
n * n
end
Now I can do that with different values and I don't have to repeat n * n:
square(1) # => 1
square(2) # => 4
square(3) # => 9
= is an assignment.
s = 0 basically says, behind the name s, there is now a zero.
0..n-1 - constructs a range that holds all numbers between 0 and n - 1. For example:
puts (0..3).to_a
# 0
# 1
# 2
# 3
for assigns i each consecutive value of the range. It loops through all values. So first i is 0, then 1, then ... n - 1.
s += i is a shorthand for s = s + i. In other words, increments the existing value of s by i on each iteration.
The s at the end just says that the method (remember the thing we opened with def) will give you back the value of s. In other words - the sum we accumulated so far.
There is your programming lesson in 5 minutes.
This example isn't idiomatic Ruby code even if it is syntactically valid. Ruby hardly ever uses the for construct, iterators are more flexible. This might seem strange if you come from another language background where for is the backbone of many programs.
In any case, the program breaks down to this:
# Define a method called a which takes an argument n
def a(n)
# Assign 0 to the local variable s
s = 0
# For each value i in the range 0 through n minus one...
for i in 0..n-1
# ...add that value to s.
s += i
end
# The result of this method is s, the sum of those values.
s
end
The more Ruby way of expressing this is to use times:
def a(n)
s = 0
# Repeat this block n times, and in each iteration i will represent
# a value in the range 0 to n-1 in order.
n.times do |i|
s += i
end
s
end
That's just addressing the for issue. Already the code is more readable, mind you, where it's n.times do something. The do ... end block represents a chunk of code that's used for each iteration. Ruby blocks might be a little bewildering at first but understanding them is absolutely essential to being effective in Ruby.
Taking this one step further:
def a(n)
# For each element i in the range 0 to n-1...
(0..n-1).reduce |sum, i|
# ...add i to the sum and use that as the sum in the next round.
sum + i
end
end
The reduce method is one of the simple tools in Ruby that's quite potent if used effectively. It allows you to quickly spin through lists of things and compact them down to a single value, hence the name. It's also known as inject which is just an alias for the same thing.
You can also use short-hand for this:
def a(n)
# For each element in the range 0 to n-1, combine them with +
# and return that as the result of this method.
(0..n-1).reduce(&:+)
end
Where here &:+ is shorthand for { |a,b| a + b }, just as &:x would be short for { |a,b| a.x(b) }.
As you are a beginner in Ruby, let's start from the small slices.
0..n-1 => [0, n-1]. E.g. 0..3 => 0, 1, 2, 3 => [0, 3]
for i in 0.. n-1 => this is a for loop. i traverses [0, n-1].
s += i is same as s = s + i
So. Method a(n) initializes s = 0 then in the for loop i traverse [0, n - 1] and s = s + i
At the end of this method there is an s. Ruby omits key words return. so you can see it as return s
def a(n)
s = 0
for i in 0..n-1
s += i
end
s
end
is same as
def a(n)
s = 0
for i in 0..n-1
s = s + i
end
return s
end
a(4) = 0 + 1 + 2 + 3 = 6
Hope this is helpful.
The method a(n) calculates the sums of the first n natural numbers.
Example:
when n=4, then s = 0+1+2+3 = 6
Let's go symbol by symbol!
def a(n)
This is the start of a function definition, and you're defining the function a that takes a single parameter, n - all typical software stuff. Notably, you can define a function on other things, too:
foo = "foo"
def foo.bar
"bar"
end
foo.bar() # "bar"
"foo".bar # NoMethodError
Next line:
s = 0
In this line, you're both declaring the variable s, and setting it's initial value to 0. Also typical programming stuff.
Notably, the value of the entire expression; s = 0, is the value of s after the assignment:
s = 0
r = t = s += 1 # You can think: r = (t = (s += 1) )
# r and t are now 1
Next line:
for i in 0..n-1
This is starting a loop; specifically a for ... in ... loop. This one a little harder to unpack, but the entire statement is basically: "for each integer between 0 and n-1, assign that number to i and then do something". In fact, in Ruby, another way to write this line is:
(0..n-1).each do |i|
This line and your line are exactly the same.
For single line loops, you can use { and } instead of do and end:
(0..n-1).each{|i| s += i }
This line and your for loop are exactly the same.
(0..n-1) is a range. Ranges are super fun! You can use a lot of things to make up a range, particularly, time:
(Time.now..Time.new(2017, 1, 1)) # Now, until Jan 1st in 2017
You can also change the "step size", so that instead of every integer, it's, say, every 1/10:
(0..5).step(0.1).to_a # [0.0, 0.1, 0.2, ...]
Also, you can make the range exclude the last value:
(0..5).to_a # [0, 1, 2, 3, 4, 5]
(0...5).to_a # [0, 1, 2, 3, 4]
Next line!
s += i
Usually read aloud a "plus-equals". It's literally the same as: s = s + 1. AFAIK, almost every operator in Ruby can be paired up this way:
s = 5
s -= 2 # 3
s *= 4 # 12
s /= 2 # 6
s %= 4 # 2
# etc
Final lines (we'll take these as a group):
end
s
end
The "blocks" (groups of code) that are started by def and for need to be ended, that's what you're doing here.
But also!
Everything in Ruby has a value. Every expression has a value (including assignment, as you saw with line 2), and every block of code. The default value of a block is the value of the last expression in that block.
For your function, the last expression is simply s, and so the value of the expression is the value of s, after all is said and done. This is literally the same as:
return s
end
For the loop, it's weirder - it ends up being the evaluated range.
This example may make it clearer:
n = 5
s = 0
x = for i in (0..n-1)
s += i
end
# x is (0..4)
To recap, another way to write you function is:
def a(n)
s = 0
(0..n-1).each{ |i| s = s + i }
return s
end
Questions?
Imagine you have integers split into arrays like 100 -> [1, 0, 0]
How do you write a recursive function that increments the long integer. eg incr([9, 9]) -> [1, 0, 0]?
I know how to do it non recursively.
This is a sample implementation of #Mbo's algorithm in Python:
def addOne(a, ind, carry):
if ind<0:
if carry > 0:
a.insert(0, carry)
else:
n = a[ind] + carry
a[ind] = n%10
carry = n/10
addOne(a, ind-1, carry)
n = int(raw_input("Enter a number: "))
a = []
if n == 0:
a.append(0)
while n>0:
a.append(n%10)
n = n/10
a = list(reversed(a))
print "Array", a
# performing addition operation
addOne(a,len(a)-1,1)
print "New Array", a
Note: I am sending 1 as the carry initially, because we want to add 1 to the number.
Sample Input/Output
Enter a number: 99
Array [9, 9]
New Array [1, 0, 0]
pseudocode
function Increment(A[], Index)
if Index < 0
A = Concatenation(1, A)
else
if (A[Index] < 9)
A[Index] = A[Index] + 1
else
A[Index] = 0
Increment(A, Index - 1)
call
Increment(A, A.Length - 1)
You might do with the following JS function, which is even a tail call optimized recursive one.
var arr = [7,8,9],
brr = [9,9,9];
function increment(a,r = []){
return a.length ? (a[a.length-1] + 1) % 10 ? (a[a.length-1]++,a.concat(r))
: increment(a.slice(0,a.length-1),r.concat(0))
: [1].concat(r);
}
console.log(increment(arr))
console.log(increment(brr))
Please keep in mind that for easy readability purposes i have used increment(a.slice(0,a.length-1),r.concat(0)) however best would be to do the job like increment(a.slice(0,a.length-1),(r.push(0),r)) which would boost the speed of incrementing a 10K 9 items array i.e. [9,9,...9] from ~1800msec to ~650msec. Also instead of [1].concat(r) you may choose use (r.unshift(1),r) which has a slight performance boost on FF (figures below 600msec) but may be not so in Chrome, yet more over you will not be creating a new array but pass a reference to r.
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]
def longest_collatz_sequence(n)
longest_sequence = []
(1..n).each do |a|
sequence = [a]
until sequence.last == 1
if a % 2 == 0
sequence.push(a/2)
else
sequence.push(3 * a + 1)
end
end
if sequence.length > longest_sequence.length
longest_sequence = sequence
end
end
longest_sequence
end
longest_collatz_sequence(n) works for n = 2, but doesn't work for n > 2. What am I doing wrong?
Thanks!
You always push the same number, a/2 or 3*a + 1 and obviously it never stops if a/2 != 1 != 3*a + 1. You probably want to use the last number of sequence instead of a:
if sequence.last % 2 == 0
sequence.push(sequence.last/2)
else
sequence.push(3 * sequence.last + 1)
end
irb> longest_collatz_sequence(3)
=> [3, 10, 5, 16, 8, 4, 2, 1]
These kinds of bugs can be tracked down by using a debugger or introducing print statements at appropriate places, so that you can trace what is going on in your program. I think this might help you help yourself in the future, because not all bugs are easy to find using visual inspection alone.
I'm new to ruby so I'm probably making a very newbie mistake here but I tried Googling for an answer and couldn't figure out the reason why this code is giving weird behavior. This code is very simple, and uses basic dynamic programming to store intermediate result to a Hash so it is used later to speed up the computation.
$existingSequence = {0 => 1, 1 => 2}
def fib(n)
if $existingSequence.has_key? n
return $existingSequence.values_at n;
end
if n == 0
return 1;
elsif n == 1
return 2;
end
$existingSequence[n] = fib(n - 1) + fib(n - 2)
return $existingSequence[n];
end
n = fib(2)
puts n
I expect this code to output 3 since that makes a call to fib(1) and fib(0) which returns 2 and 1 respectively, and then added to be 3. But the output is 1 and 2.
Hash.values_at returns an array, so when the code does fib(1) + fib(0), it's concatenating the arrays [2] and [1] together, resulting in the answer [2, 1]. Instead of:
return $existingSequence.values_at n;
...you should do this instead:
return $existingSequence[n]
BTW, the Fibonacci sequence traditionally starts with 0 and 1, not 1 and 2.
Slightly off-topic, here's a fun way of doing essentially the same thing, but using the Hash default value mechanism to use the Hash not only for caching, but also for computing the values:
fibs = { 0 => 0, 1 => 1 }.tap do |fibs|
fibs.default_proc = ->(fibs, n) { fibs[n] = fibs[n-1] + fibs[n-2] }
end
fibs[9]
# => 34
Note: I didn't come up with this myself, I stole it from here.
The second line of fib should read:
return $existingSequence[n]
instead of
return $existingSequence.values_at n
Add puts $existingSequence to the end of the file to see the difference.