Recursion in Ruby - ruby

I am writing a selection sort, and I get it to work when I just pass in an array, but when I try to use recursion, it gives me a stack too deep error. What am I doing wrong with this?
def selectionSortRecursive(array, arrayPosition)
if arrayPosition == (array.length-1)
puts "End of the line folks!"
return array
end
while arrayPosition >= 1 && array[arrayPosition] < array[arrayPosition - 1] do
puts "This is pass #{arrayPosition}"
if array[arrayPosition] < array[arrayPosition - 1]
tmp = array[arrayPosition]
array[arrayPosition] = array[arrayPosition - 1]
array[arrayPosition - 1] = tmp
end # end if
arrayPosition += 1
end
selectionSortRecursive(array, arrayPosition)
return array
end
This is what I am using to test it:
selectionSortRecursive(array, 1)

In such situations, just put print statements to see which code is executed and with which values. You would have seen :
$ ruby -w t4.rb
t4.rb:21: warning: mismatched indentations at 'end' with 'while' at 10
initial array=[4, 3, 2, 1], initial position=2
last_pos=3
This is pass 2 cur=2 pre=3
after switching elements : [4, 2, 3, 1]
This is pass 3 cur=1 pre=3
after switching elements : [4, 2, 1, 3]
initial array=[4, 2, 1, 3], initial position=4
last_pos=3
initial array=[4, 2, 1, 3], initial position=4
last_pos=3
initial array=[4, 2, 1, 3], initial position=4
last_pos=3
initial array=[4, 2, 1, 3], initial position=4
last_pos=3
.......
t4.rb:2: stack level too deep (SystemStackError)
To stop a recursion, you need to check some condition. Look at the millions of factorial or fibonacci examples which are always used to explain the recursion.
I'm not sure to understand how you want to sort, but this code works :
def selectionSortRecursive(array, arrayPosition)
puts "initial array=#{array}, initial position=#{arrayPosition}"
initial_array_position = arrayPosition
last_pos = array.length - 1
puts "last_pos=#{last_pos}"
if arrayPosition == (last_pos)
puts "End of the line folks!"
return array
end
while arrayPosition >= 1 && arrayPosition <= last_pos && array[arrayPosition] < array[arrayPosition - 1] do
cur = array[arrayPosition]
pre = array[arrayPosition - 1]
puts "This is pass #{arrayPosition} cur=#{cur} pre=#{pre}"
if array[arrayPosition] < array[arrayPosition - 1]
tmp = array[arrayPosition]
array[arrayPosition] = array[arrayPosition - 1]
array[arrayPosition - 1] = tmp
puts "after switching elements : #{array}"
end # end if
arrayPosition += 1
end
selectionSortRecursive(array, arrayPosition) if arrayPosition != initial_array_position
return array
end
p selectionSortRecursive([4,3,2,1], 2)
Execution :
$ ruby -w t4.rb
initial array=[4, 3, 2, 1], initial position=2
last_pos=3
This is pass 2 cur=2 pre=3
after switching elements : [4, 2, 3, 1]
This is pass 3 cur=1 pre=3
after switching elements : [4, 2, 1, 3]
initial array=[4, 2, 1, 3], initial position=4
last_pos=3
[4, 2, 1, 3]

Related

Algorithm for array with `while` or `until` loop

I have:
array = [1, 4, -1, 3, 2]
I want a new array that follows the following logic:
First element is located at index 0, so it is 1.
Second element is located at index 1 (because value for index 0 was 1).
Third element is located at index 4, so it is 2.
And so on until the loop meets value -1, which is the last value, and it should brake.
The new array should be:
[1, 4, 2, -1]
I have:
def task(a)
array = []
a.each_with_index do |v, i|
result = a[i]
until a[i] == -1
array << a[result]
end
end
puts result
end
As others say, you need to change the index in your loop. Also, if you want -1 in the result, you should exit at bottom. And with_index will give you indices in order, which is not what you want here. This will do what you want:
def task(a)
i = 0
array = []
begin
i = a[i]
array << i
end until i == -1
array
end
p task([1, 4, -1, 3, 2])
# => [1, 4, 2, -1]
until a[i] == -1
array << a[result]
end
This code is looping eternally - there is nothing to change i .
As discussed in the comments, you are looping through the array which is not what you require.
You could use a recursive method to handle jumping from one element to another based on previous value. Consider the following:
arr = [1, 4, -1, 3, 2]
def task(arr, n=0, result=[])
if arr[n] == -1
return result + [-1]
end
r = arr[n]
task(arr, r, result + [r])
end
puts task(arr)
input_array = [1, 4, -1, 3, 2]
last_valid_index = input_array.find_index { |entry| entry < 0 }
first_element = input_array.first
last_element = input_array[last_valid_index]
middle_elements = (1..last_valid_index).map { |i| input_array[input_array[i-1]]}
output_array = [first_element] + middle_elements + [last_element]
p output_array
# => [1, 4, 2, -1]
you could to most of it on one line like so, but I think the more verbose version is more self documenting.
input_array = [1, 4, -1, 3, 2]
last_valid_index = input_array.find_index { |entry| entry < 0 }
output_array = [input_array.first] + (1..last_valid_index).map { |i| input_array[input_array[i-1]]} + [input_array[last_valid_index]]
p output_array
# => [1, 4, 2, -1]
I'd suggest this option, just to avoid infinite loops or index out range:
i, ary = 0, [array[0]]
array.size.times do
break if array[i] == -1 or array[i] > array.size - 1
i = array[i]
ary << array[i]
end
ary #=> [1, 4, 2, -1]
An infinite loop happens for example when array = [1, 4, -1, 0, 3].
Index out of range can happen when array = [1, 4, 6, 3, 2]

Codility: CountDistinctSlices. What am I missing?

The Codility Question Is Here: https://codility.com/programmers/lessons/15-caterpillar_method/count_distinct_slices/
Now, my solution is below:
def solution(m, a)
end_idx = 0
hash_of_elements = {}
last_idx = a.size - 1
slice_right_now = []
slice_counter = 0
while last_idx >= end_idx
el_to_add = a[end_idx]
while !hash_of_elements[el_to_add].nil?
element_to_remove = slice_right_now.shift
hash_of_elements.delete element_to_remove
#puts "removing #{element_to_remove} from the slice. the new slice is #{slice_right_now}. Hash is #{hash_of_elements.inspect}"
puts "#{slice_right_now.inspect}" if slice_right_now.size > 1
if slice_right_now.size > 1
slice_counter += 1
return 1000000000 if slice_counter > 1000000000
end
end
#puts "Adding #{el_to_add} to the list!"
hash_of_elements[el_to_add] = true
slice_right_now << el_to_add
puts "#{slice_right_now.inspect}" if slice_right_now.size > 1
if slice_right_now.size > 1
slice_counter += 1
return 1000000000 if slice_counter > 1000000000
end
end_idx += 1
end
puts "Number of slices other than indivisual elments are #{slice_counter}"
slice_counter += a.size
end
It is a Ruby Solution. For the input: 6, [1, 3, 4, 1, 2, 1, 3, 2, 1]
It gets the following slices:
[1, 3]
[1, 3, 4]
[3, 4]
[3, 4, 1]
[3, 4, 1, 2]
[4, 1, 2]
[1, 2]
[2, 1]
[2, 1, 3]
[1, 3]
[1, 3, 2]
[3, 2]
[3, 2, 1]
In addition to this, each element of the array is a slice also.
The answer is wrong however apparently.
The answer to that input is supposed to be 24. Mine is 22. I don't understand what I am missing.
24 is correct, as you can easily check with brute force solutions that go over all slices and count the distinct ones:
(1..a.size).sum { |k| a.each_cons(k).count { |s| !s.uniq! } }
=> 24
(1..a.size).sum { |k| a.each_cons(k).reject(&:uniq!).count }
=> 24
(0...a.size).sum { |i| (i...a.size).count { |j| !a[i..j].uniq! } }
=> 24
(0...a.size).to_a.repeated_combination(2).count { |i, j| !a[i..j].uniq! }
=> 24
(0..a.size).to_a.combination(2).count { |i, j| !a[i...j].uniq! }
=> 24
If you don't just count but print them, you'll see that you're missing the slice consisting of [4, 1] and the slice consisting of [2, 1] at the end.
The fishing lesson is: If the problematic case is small enough that you can solve it with trivial brute force, do do that and compare its findings with your more clever attempt's findings.

Distributions using nested loops

I would like to write a program which generates all distributions for a given n.
For example, if I enter n equal to 7, the returned result will be:
7
6 1
5 2
5 1 1
4 3
4 2 1
4 1 1 1
3 3 1
3 2 2
3 2 1 1
3 1 1 1 1
2 2 2 1
2 2 1 1 1
2 1 1 1 1 1
1 1 1 1 1 1 1
I wrote the following code:
def sum(a, n)
for i in 1..a.length
a.each do |a|
z = a+i
if z == n
print i
puts a
end
end
end
end
def distribution(n)
numbers_container = []
for i in 1..n-1
numbers_container<<i
end
sum(numbers_container,n)
end
puts "Enter n"
n = gets.chomp.to_i
distribution(n)
I'm stuck in the part where the program needs to check the sum for more than two augends. I don't have an idea how can I write a second loop.
I suggest you use recursion.
Code
def all_the_sums(n, mx=n, p=[])
return [p] if n.zero?
mx.downto(1).each_with_object([]) { |i,a|
a.concat(all_the_sums(n-i, [n-i,i].min, p + [i])) }
end
Example
all_the_sums(7)
#=> [[7],
# [6, 1],
# [5, 2], [5, 1, 1],
# [4, 3], [4, 2, 1], [4, 1, 1, 1],
# [3, 3, 1], [3, 2, 2], [3, 2, 1, 1], [3, 1, 1, 1, 1],
# [2, 2, 2, 1], [2, 2, 1, 1, 1], [2, 1, 1, 1, 1, 1],
# [1, 1, 1, 1, 1, 1, 1]]
Explanation
The argument mx is to avoid the generation of permuations of results. For example, one sequence is [4,2,1]. There are six permutations of the elements of this array (e.g., [4,1,2], [2,4,1] and so on), but we want just one.
Now consider the calculations performed by:
all_the_sums(3)
Each eight-space indentation below reflects a recursive call to the method.
We begin with
n = 3
mx = 3
p = []
return [[]] if 3.zero? #=> no return
# first value passed block by 3.downto(1)..
i = 3
a = []
# invoke all_the_sums(0, [0,3].min, []+[3])
all_the_sums(0, 0, [3])
return [[3]] if 0.zero? #=> return [[3]]
a.concat([[3]]) #=> [].concat([[3]]) => [[3]]
# second value passed block by 3.downto(1)..
i = 2
a = [[3]]
# invoke all_the_sums(1, [1,2].min, []+[2])
all_the_sums(1, 1, [2])
return [[2]] if 1.zero? #=> do not return
# first and only value passed block by 1.downto(1)..
i = 1
a = []
# invoke all_the_sums(0, [0,1].min, [2]+[1])
all_the_sums(0, 0, [2,1])
return [[2,1]] if 0.zero? #=> [[2,1]] returned
a.concat([[2,1]]) #=> [].concat([[2,1]]) => [[2,1]]
return a #=> [[2,1]]
a.concat([[2,1]]) #=> [[3]].concat([[2,1]]) => [[3],[2,1]]
# third and last value passed block by 3.downto(1)..
i = 1
a = [[3],[2,1]]
# invoke all_the_sums(2, [2,1].min, [1])
all_the_sums(2, 1, [1])
return [] if 2.zero? #=> [] not returned
# first and only value passed block by 1.downto(1)..
i = 1
a = []
# invoke all_the_sums(1, [1,1].min, [1]+[1])
all_the_sums(1, 1, [1,1])
return [1,1] if 1.zero? #=> [1,1] not returned
# first and only value passed block by 1.downto(1)..
i = 1
a = []
# invoke all_the_sums(0, [0,1].min, [1,1]+[1]])
all_the_sums(0, 0, [1,1,1])
return [1,1,1] if 1.zero?
#=> return [1,1,1]
a.concat([[1,1,1]]) #=> [[1,1,1]]
return a #=> [[1,1,1]]
a.concat([[1,1,1]]) #=> [].concat([[1,1,1]]) => [[1,1,1]]
return a #=> [[1,1,1]]
a.concat([[1,1,1]]) #=> [[3],[2,1]].concat([[1,1,1]])
return a #=> [[3],[2,1],[1,1,1]]
You can use unary with parameters to have infinite amounts of parameters:
def test_method *parameters
puts parameters
puts parameters.class
end
test_method("a", "b", "c", "d")
So, parameters inside the block becomes an array of parameters. You can then easly loop through them:
parameters.each { |par| p par }
Also, don't use for loops for this as they are less readable than using each methods.
[1..n-1].each do i
# body omitted
end
I think you be able to work it out if you tried to call sum recursively. After this bit:
print i
puts a
Try calling sum again, like this:
sum((1..a).to_a, a)
It won't solve it, but it might lead you in the right direction.

Generating combinations from an array which == a specified amount?

I need to get all the possible number combinations from denom_arr which equal the amt.
denom_arr = [4,3,1]
amt = 10
This case would produce:
[4, 4, 1, 1]
[3, 3, 3, 1]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
[4, 3, 1, 1, 1]
[4, 3, 3]
. . . (other cases...)
Problem is the code I wrote is breaking after 1-3 and I'm not sure how to make it loop over the same index to get case 4-6+
set, sets = [], []
i = 0
loop do
i = 0 if denom_arr[i].nil?
loop do
set << denom_arr[i]
break if set.inject(:+) > amt
end
set.pop if set.inject(:+) > amt
if set.inject(:+) == amt
sets << set
set = []
denom_arr.shift
end
i += 1
sets
break if denom_arr.empty?
end
UPDATE
I know this can be done with recursion with memoization/dynamic programming techniques, but I am trying to do this strictly in a loop for the sake of testing a theory.
I would do this recursively
def possible_sums(arr, amt)
return [[]] if amt == 0
return [] if amt < 0
arr.reduce([]) do |sums, e|
sums.concat(
possible_sums(arr, amt-e)
.map { |sum| sum.unshift(e).sort }
)
end.uniq
end
p possible_sums([4,3,1], 10)
# => [
# [1, 1, 4, 4], [3, 3, 4], [1, 1, 1, 3, 4], [1, 1, 1, 1, 1, 1, 4],
# [1, 3, 3, 3], [1, 1, 1, 1, 3, 3], [1, 1, 1, 1, 1, 1, 1, 3],
# [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
# ]
Although this is potentially inefficient in that it repeats work, this can be alleviated by using dynamic programming (essentially, memoizing the results of the recursive function).
UPDATE Here is an iterative solution:
def possible_sums_it(arr, amt)
sums = Array.new(amt+1) { [] }
sums[0] << []
(1..amt).each do |i|
arr.each do |e|
if i-e >= 0
sums[i].concat(
sums[i-e].map { |s| [e, *s].sort }
)
end
end
sums[i].uniq!
end
sums[amt]
end
This is in fact the dynamic programming algorithm for the problem.
So if you squint at it just right, you'll see that essentially what it is doing, is calculating all the possible sums for 0 up to amt into the sums array, using what is basically the recursive algorithm, but instead of the recursive call, we lookup a value in sums that we have calculated beforehand.
This works because we know that we won't need sums[i] before sums[j] for j < i.

How to take one element out of an array and put in front?

a = [1,2,3]
b = [2,1,3]
What is the best way to get b from a?
My inelegant solution:
x = 2
y = a - [x]
b = y.unshift(x)
a.unshift a.delete(2)
This appends the recently deleted object (here 2).
Beware that, if the object in question appears more than once in the array, all occurences will be deleted.
In case you want only the first occurrence of an object to be moved, try this:
a = [1,2,3,2]
a.unshift a.delete_at(a.index(2))
# => [2, 1, 3, 2]
a.unshift a.slice!(a.index(2)||0)
# => [2, 1, 3]
If there are multiple instances, only the first instance is moved to the front.
If the element doesn't exist, then a is unchanged.
If you wanted to move elements of an array arbitrarily, you could do something like this:
Code
# Return a copy of the receiver array, with the receiver's element at
# offset i moved before the element at offset j, unless j == self.size,
# in which case the element at offset i is moved to the end of the array.
class Array
def move(i,j)
a = dup
case
when i < 0 || i >= size
raise ArgumentError, "From index is out-of-range"
when j < 0 || j > size
raise ArgumentError, "To index is out-of-range"
when j < i
a.insert(j, a.delete_at(i))
when j == size
a << a.delete_at(i)
when j > i+1
a.insert(j-1, a.delete_at(i))
else
a
end
end
end
With Ruby v2.1, you could optionally replace class Array with refine Array. (Module#refine was introduced experimentally in v2.0, but was changed substantially in v2.1.)
Demo
arr = [1,2,3,4,5] #=> [1, 2, 3, 4, 5]
arr.move(2,1) #=> [1, 3, 2, 4, 5]
arr.move(2,2) #=> [1, 2, 3, 4, 5]
arr.move(2,3) #=> [1, 2, 3, 4, 5]
arr.move(2,4) #=> [1, 2, 4, 3, 5]
arr.move(2,5) #=> [1, 2, 4, 5, 3]
arr.move(2,6) #=> ArgumentError: To index is out-of-range

Resources