recursive bubble sort ruby - ruby

I am trying to build a recursive bubble sorting method in Ruby. It makes it through one pass fine, but it keeps returning after that. It seems that my
if array == swapped_array is being triggered, but I don't understand where my array variable is being redefined.
def bubble_sort(array, swapped = true)
return array if swapped == false
i = 0
if swapped == true
swapped_array = comparator(array)
end
if array == swapped_array
swapped = false
bubble_sort(array, swapped)
else bubble_sort(swapped_array)
end
end
def comparator(array, i = 0)
return array if i == array.length - 1
if array[i] > array[i+1]
array[i], array[i+1] = array[i+1], array[i]
end
i += 1
comparator(array, i)
end

swapped_array = comparator(array)
... takes the return value of comparator and assigns it to swapped_array. But comparator (eventually) returns the original argument array, which is the same array as the one defined in the caller. So
array == swapped_array
is always true.
If you want to compare two different arrays, you can call .dup on the array. This creates a new object with the same values as the original.

It is because in comparator, you are swapping the elements in the original array. Whatever you do to it, array and swapped_array in bubble_sort refer to the same array instance, and hence will always be the same. To solve the problem, add a single line to the top of your definition:
def comparator(array, i = 0)
array = array.dup
...
end

Related

What does this snippet of code in the children variable array mean?

def breadth_first_search(adj_matrix, source_index, end_index)
node_queue = [source_index]
puts "#{source_index} stored into queue \n"
loop do
curr_node = node_queue.pop
puts "#{curr_node} popped \n"
return false if curr_node == nil
return true if curr_node == end_index
children = (0..adj_matrix.length-1).to_a.select do |i|
adj_matrix[curr_node][i] == 1
end
puts "children: #{children}"
node_queue = children + node_queue
puts "node_queue: #{node_queue}"
end
end
I can understand what it does conceptually, but the actual syntax is what I don't understand. Is the children variable an array that stores elements with a loop?
The variable children is being set to the result of the enumerable method select
select is a method often called on an array which takes a block. The return value is only the values of the original array in which the block returns a truthy object (e.g. not false, not nil)
(0..adj_matrix.length-1) is a range object, from 0 to the length of adj_matrix, which is converted to an array via to_a (resulting in [0, 1, 2, ...])
Then that array is filtered by select to only be the values in which adj_matrix[curr_node][i] == 1. So if the nodes at index 0 and 2, are equal to 1
then the result would be [0, 2]

Is each slower than while in Ruby?

The following two functions, which check if a number is prime:
def prime1?(prime_candidate)
return true if [1, 2].include? prime_candidate
range = 2.upto(Math.sqrt(prime_candidate).ceil).to_a
i = 0
while i < range.count
return false if prime_candidate % range[i] == 0
range = range.reject { |j| j % range[i] == 0 }
end
true
end
def prime2?(prime_candidate)
return true if [1, 2].include? prime_candidate
range = 2.upto(Math.sqrt(prime_candidate).ceil).to_a
range.each do |i|
return false if prime_candidate % i == 0
range = range.reject { |j| j % i == 0 }
end
true
end
yield the following benchamrking result when testing with a very large prime (5915587277):
user system total real
prime1: 2.500000 0.010000 2.510000 ( 2.499585)
prime2: 20.700000 0.030000 20.730000 ( 20.717267)
Why is that? Is it because in the second function range does not get modified by the reject, so the each is iterating over the original long range?
When you do range=range.reject {..}, you don't modify the parent range (which you shouldn't do, because it would mess up the iteration--you need reject! to do that) but rather construct a temporary array which only gets assigned to the parent range variable at the end of the iteration.
The each iteration in prime2 runs over the whole original range, not the shortened which, before the loop ends, only exist in the block.
The while version modifies the original array and is therefore quicker (BTW, you realize that i remains zero and it's the range.count that changes (decreases) in that while condition and that reject iterates over the WHOLE array again--even the beginning where there couldn't possibly be any more nonprimes to reject).
You'll get a much faster result if you improve the logic of your code. That array manipulation is costly and for this, you don't even need an array:
def prime3?(prime_candidate)
return false if prime_candidate==1
return true if prime_candidate==2
range = 2..(Math.sqrt(prime_candidate).ceil)
range.all? {|x| prime_candidate % x !=0 }
end #about 300× times as fast for your example as prime1? on my PC

Problems with a sorting exercise using swapping

So I am trying to create a simple sorting program using the swapping method and I just cant get it to work properly. The code seems solid and it works up until it gets to artichoke. I've gone through and 'puts' the value of array during each iteration and pear swaps with apple, then swaps with orange, then swaps with peach and grapefruit, but when it gets to artichoke it refuses to swap like it should. It also doesn't seem like its an issue with it being the final item in the list because if I add, lets say, banana to the end it still stops at artichoke. Ultimately I want to nest the function inside an 'm.times do' function to continue the swapping until the entire list is sorted but for some reason when I put
m.times do
end
surrounding the 'n.times do' it creates another error. But I guess it doesn't even matter if I cant even get artichoke to swap with pear. Here is my code below (yes I am aware there is a .sort function, this is for learning purposes).
## Ignore this part, it is just a function I made and commented out to create
## your own list.
=begin
array = []
while true
puts "what item would you like to add to your list to be sorted?"
puts "press enter without entering an item to quit"
item = gets.chomp
break if item.empty?
array.push item
end
=end
##
array = ['pear' , 'apple' , 'orange' , 'peach' , 'grapefruit' , 'artichoke']
i = 0
m = array.length
n = m - 1
n.times do
if array.to_s[i] >= array.to_s[i+1]
swap = array[i]
array[i] = array[i+1]
array[i+1] = swap
end
i += 1
end
puts array
array = ['pear' , 'apple' , 'orange' , 'peach' , 'grapefruit' , 'artichoke']
m = array.length
n = m - 1
n.times do
i = 0
n.times do
if array[i] >= array[i+1]
temp = array[i]
array[i] = array[i+1]
array[i+1] = temp
end
i += 1
end
end
puts array
In this method of sorting, called "bubble sorting", you need nested loops. After just one run only the topmost element is guaranteed to be at its correct position. This is also known as infamous O(n*n) complexity of this poor algorithm. Btw. The inner loop can be shorther, like n-i times.
Fixed some mistakes:
array.to_s[i] is wrong because index has to be applied to array, not to_s method
... and its not necessary to call to_s since elements of this particular array already are strings
i = 0 had to be moved to the beggining of the outer loop
term "swap" refers to action, while variable is usually called "temp"
I think you mean to do array[i] >= array[i+1]
array.to_s will make a string out of your entire array and so you are comparing a character at a time of your stringified array instead of a word at a time as you seem to be intending to do.

Setting optional method arguments?

I am trying to define a method to sum the elements of a given array. So far I have:
def sum2
return self if self.length <=1
i = self.length
#sum2 = sum2(i-1) + self[i]
end
I'm getting an invalid argument error for the second call of sum2(i-1). Is there any way I can set it to take either 0 or 1 arguments? Or, am I going about this the wrong way?
It's not totally clear from the question phrasing, but I assume sum2 is a method in an array class that you've defined yourself. So, I assume it's derived from a Ruby Array class.
Currently, your method returns the array self if it has no more than one element. Technically, the array of one element isn't the sum of elements. So you don't want to return self in that case.
But you can use Ruby's array methods to simplify:
def sum2
self.inject(:+)
end
This will return nil if the array has zero length, and the sum of elements otherwise. If you want to return 0 on a length 0 array, then:
def sum2
return (self.length == 0) ? 0 : self.inject(:+)
end
Or more simply, per #toro2k's comment:
def sum2
self.inject(0, :+)
end

Define a method `sum_to_n?`

I have the code below. Method sum_to_n? takes an array arr of integers and an integer n as arguments and returns true if any two elements in arr sum to n. It should return true for empty arr with zero n, but keeps returning false.
def sum_to_n?(arr, n)
hash = Hash.new(0)
arr.each do |val|
if hash.key? val
return true
else
hash[n-val] = val
end
end
return false
end
What am I doing wrong?
shorter version:
def sum_to_n?(arr, n)
(arr.empty? && n.zero?) || arr.permutation(2).any? { |a, b| a + b == n }
end
Your code is (almost) correct, but your expectation is wrong. Your code returns true when there are two (or one) element that adds up to n. If you pass an empty array, then there will not be any element(s) that add up to n (because there is no element in the array in the first place). Hence, you get false.
If you want it to return true for an empty array, then that would be an exceptional behavior that does not follow logically. You will have to put a condition such as
return true if arr.empty?
in your code.

Resources