Problems with a sorting exercise using swapping - ruby

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.

Related

How do I interpret this pseudocode in Ruby?

I don't quite understand how to "initialize a multidimensional array to equal 1" as the initial for loops seem to suggest here. I haven't learned to properly read pseudocode, and I don't fully understand how this program works.
function countRoutes(m,n)
grid ← array[m + 1][n + 1]
for i = 0 to m do
grid[i][0] ← 1
end for
for j = 0 to n do
grid[0][j] ← 1
end for
for i = 1 to m do
for j = 1 to n do
grid[i][j] ← grid[i − 1][j] + grid[i][j − 1]
end for
end for
return grid[m][n]
end function
Thanks for your help!
This isn't hard to translate.. Ruby uses = instead of left arrow for assignment, and def instead of function to define a subroutine (which it calls a method instead of a function), but that's about it. Let's go through it.
function countRoutes(m,n)
That's beginning a function definition. In Ruby we use a method instead, and the keyword to define such a thing is def. It's also convention in Ruby to use snake_case for multiword names instead of camelCase:
def count_routes(m, n)
Now to create the grid:
grid ← array[m + 1][n + 1]
Ruby arrays are dynamic, so you don't normally need to specify the size at creation time. But that also means you don't get initialization or two-dimensionality for free. So what we have to do here is create an array of m+1 arrays, each of which can be empty (we don't need to specify that the sub-arrays need to hold n+1 items). Ruby's Array constructor has a way to do just that:
grid = Array.new(m+1) do [] end
Now the initialization. Ruby technically has for loops, but nobody uses them. Instead, we use iterator methods. For counting loops, there's a method on integers called times. But the pseudocode counts from 0 through m inclusive; times also starts at 0, but only counts up to one less than the invocant (so that way when you call 3.times, the loop really does execute "three times", not four). In this case, that means to get the behavior of the pseudocode, we need to call times on m+1 instead of m:
(m+1).times do |i|
grid[i][0] = 1
end
As an aside, we could also have done that part of the initialization inside the original array creation:
grid = Array.new(m+1) do [1] end
Anyway, the second loop, which would be more awkward to incorporate into the original creation, works the same as the first. Ruby will happily extend an array to assign to not-yet-existent elements, so the fact that we didn't initialize the subarrays is not a problem:
(n+1).times do |j|
grid[0][j] = 1
end
For the nested loops, the pseudocode is no longer counting from 0, but from 1. Counting from 1 through m is the same number of loop iterations as counting from 0 through m-1, so the simplest approach is to let times use its natural values, but adjust the indexes in the assignment statement inside the loop. That is, where the pseudocode starts counting i from 1 and references i-1 and i, the Ruby code starts counting i from 0 and references i and i+1 instead.
m.times do |i|
n.times do |j|
grid[i+1][j+1] = grid[i][j+1] + grid[i+1][j]
end
end
And the return statement works the same, although in Ruby you can leave it off:
return grid[m][n]
end
Putting it all together, you get this:
def count_routes(m, n)
grid = Array.new(m+1) do [1] end
(n+1).times do |j|
grid[0][j] = 1
end
m.times do |i|
n.times do |j|
grid[i+1][j+1] = grid[i][j+1] + grid[i+1][j]
end
end
return grid[m][n]
end
The notation grid[i][j] ← something means assigning something to the element of grid taking place on i-th line in j-th position. So the first two loops here suggest setting all values of the first column and the first row of the grid (correspondingly, the first and the second loops) to 1.

Quicksort not working with little larger array size

Below is my quicksort code in ruby, and its working fine for array size like 20-25 but getting either stack level too deep error or its getting stuck for longer time.
I am guessing i am doing a trivial mistake but not able to figure out.
# This program is to do sorting using Quick sort.
require 'colorize'
class QuickSort
attr_accessor :array
def initialize(size)
puts "Generating Random numbers for your array".cyan
#array = (1..size.to_i).map do
rand(500) # Generating random numbers between 1 to 500.
end
# Boundary case
if #array.size == 1
puts "Your sorted array is"
p #array
return
end
puts "Array Before Sorting".yellow
p #array
#head = 0
#tail = #array.size-1
startSort(#array,#head,#tail) #Start the searching logic.
end
# Quicksort logic
def startSort(array,head,tail)
if head < tail
pivot = partitionArray(array,head,tail) # Calling the sorting logic
startSort(array,head,pivot-1)
startSort(array,pivot+1,#tail)
end
end
# This method is called to partition the array based on pivot.
def partitionArray(array,head,tail)
pivot_value = array[(head+tail)/2] # Choosing random pivot value.
# Run this partition step until head is equal to tail
while head <= tail
if array[head] < pivot_value
head += 1
elsif array[head] >= pivot_value
if array[tail] > pivot_value
tail -= 1
elsif array[tail] <= pivot_value
# Swapping head and tail values
temp = array[head]
array[head] = array[tail]
array[tail] = temp
# Moving each pointer forward from both the directions.
head += 1
tail -= 1
end
end
end
return head # Nothing but pivot
end
end
puts "Enter the size of Array"
#size = gets.chomp
# Checking if entry is a valid integer or not.
if #size.match(/^(\d)+$/)
#obj = QuickSort.new(#size)
puts "Array after sorting is ".green
p #obj.array
else
puts "Invalid Entry".red
end
Your implementation of quick sort algorithm is not correct. In a line:
startSort(array, pivot + 1, #tail)
you always call startSort method for pivot + 1 and array.size - 1 because #tail is an instance variable. It is assigned to #array.size - 1 only once and its value never changes. However, simply changing this line to
startSort(array, pivot + 1, tail)
is not enough to fix your code. With this change, it works fast even for large arrays but produces incorrect answer. This line should actually be:
startSort(array, pivot, tail).
With this change, it works fast for large arrays and sorts the array properly.

Please walk me through this code from ruby monk

def random_select(array, n)
result = []
n.times do
# I do not fully understand how this line below works or why. Thank you
result.push array[rand(array.length)]
end
result
end
You are probably confused by this part:
n.times do
result.push(array[rand(array.length)])
end
n.times says it should loop n times.
result.push says to basically "push" or "put" something in the array. For example:
a = []
a.push(1)
p a #=> [1]
In array[rand(array.length)] , rand(array.length) will produce a random number as an index for the array. Why? rand(n) produces a number from 0 to n-1. rand(5) will produce either 0,1,2,3 or 4, for example.
Arrays use 0-based indexing, so if you have an array, say a = ['x', 'y', 'z'], to access 'x' you do a[0], to access y you do a[1] and so on. If you want to access a random element from a, you do a[rand(array.length)], because a.length in this case is 3, and rand(3) will produce a number that is either 0, 1 or 2. 0 is the smallest index and 2 is the largest index of our example array.
So suppose we call this method:
random_select([6,3,1,4], 2)
Try to see this code from the inside out. When the code reaches this part:
result.push(array[rand(array.length)])
it will first execute array.length which will produce 4. It will then execute rand(array.length) or rand(4) which will get a number between 0 and 3. Then, it will execute array[rand(array.length)] or array(some_random_number_between_0_and_3) which will get you a random element from the array. Finally, result.push(all_of_that_code_inside_that_got_us_a_random_array_element) will put the random element from the array in the method (in our example, it will be either 6, 3, 1 or 4) in the results array. Then it will repeat this same process once again (remember, we told it to go 2 times through the iteration).
The code can be rewritten to be much simpler, using the block-form Array constructor:
def random_select(array, n)
Array.new(n) {array.sample}
end
This creates a new array of size n and fills it with random samples from the array.
Note that the above solution, like your sample code, selects from the entire array each time which allows duplicate selections. If you don't want any duplicate selections, it's even simpler, since it is the default behavior of Array#sample:
def random_select(array, n)
array.sample(n)
end

Replace near working array code with hash code in ruby to find mode

I was attempting to find a mode without using a hash, but now do not know if its possible, so I am wondering if someone can help me to translate my near working array code, into hash mode to make it work.
I have seen a shorter solution which I will post, but I do not quite follow it, I'm hoping this translation will help me to understand a hash better.
Here is my code, with my comments - I have bolded the part that I know will not work, as I'm comparing a frequency value, to the value of an element itself
#new = [0]
def mode(arr)
arr.each do |x| #do each element in the array
freq = arr.count(x) #set freq equal to the result of the count of each element
if freq > #new[0] && #new.include?(x) == false #if **frequency of element is greater than the frequency of the first element in #new array** and is not already there
#new.unshift(x) #send that element to the front of the array
#new.pop #and get rid of the element at the end(which was the former most frequent element)
elsif freq == #new[0] && #new.include?(x) == false #else **if frequency of element is equal to the frequency of the first element in #new array** and is not already there
#new << x #send that element to #new array
end
end
if #new.length > 1 #if #new array has multiple elements
#new.inject(:+)/#new.length.to_f #find the average of the elements
end
#new #return the final value
end
mode([2,2,6,9,9,15,15,15])
mode([2,2,2,3,3,3,4,5])
Now I have read this post:
Ruby: How to find item in array which has the most occurrences?
And looked at this code
arr = [1, 1, 1, 2, 3]
freq = arr.inject(Hash.new(0)) { |h,v| h[v] += 1; h }
arr.sort_by { |v| freq[v] }.last
But I dont quite understand it.
What I'd like my code to do, is, as it finds the most frequent element,
to store that element as a key, and its frequency as its value.
And then I'd like to compare the next elements frequency to the frequency of the existing pair,
and if it is equal to the most frequent, store it as well,
if it is greater, replace the existing,
and if it is less than, to disregard and move to the next element.
Then of course, I'd like to return the element which has most frequencies, not the amount of frequencies,
and if two or more elements share the most frequencies, then to find the average of those numbers.
I'd love to see it with some hint of my array attempt, and maybe an explanation of that hash method that I posted above, or one that is broken down a little more simply.
This seems to fit your requirements:
def mode(array)
histogram = array.each_with_object(Hash.new(0)) do |element, histogram|
histogram[element] += 1
end
most_frequent = histogram.delete_if do |element, frequency|
frequency < histogram.values.max
end
most_frequent.keys.reduce(&:+) / most_frequent.size.to_f
end
It creates a hash of frequencies histogram, where the keys are the elements of the input array and the values are the frequency of that element in the array. Then, it removes all but the most frequent elements. Finally, it averages the remaining keys.

Compare sequential last elements in ruby array

I have an array that contains values that I'm working on (in a specific order) something like the following:
myArray = [3,6,5,6,2,1]
I need to evaluate the elements in the array and determine the number of the elements to copy.
The rules are: I need to copy the elements where the sum of those elements is not greater than the previous element.
I can kind of express it like this:
if myArray[-3] > (myArray[-2] + myArray[-1])
elements_to_copy = [myArray[-2],myArray[-1]]
else
elements_to_copy = [myArray[-1]]
end
Which feels very crappy, as I can't work out how to make it work as an iterative function, so that I can continue up the chain till the comparison fails.
Can anyone help?
myArray = [3,5,6,2,1]
i = 0
myArray.reverse.inject do |sum, cur|
break if cur < sum
i -= 1
sum + cur
end
The range to copy is i..-1.
elements_to_copy = []
array = myArray.reverse
array.each_with_index do |item, index|
if array.values_at[0..[index-1,0].max].sum <= item
elements_to_copy << item
end
end
Should do the trick, if I understand you correctly.

Resources