Array iteration back to last position - ruby

I was trying to sort an array. I know I can use sort_by, but I was trying to implement it by myself. Here is the code:
def insertionSort(ar)
for i in 1..ar.length - 1
j = i
k = j - 1
while ar[j] < ar[k] && j != 0 do
temp = ar[k]
ar[k] = ar[j]
ar[j] = temp
k -= 1
j -= 1
end
puts ar.join(" ")
end
end
When the program reaches the first line and decrements, it came back to the last position, so the while doesn't break ever. I solved this with a condition in while to verify if is the first position.
Is this normal? Why does this happen? Is an array like a cycle list, and when I try to reach the -1 position, am I using the last position?

No, arrays in Ruby(like other languages) is NOT a cycle list.
It's syntax sugar. A negative index is relative to the end of the array. an index of -1 indicates the last element of the array, -2 is the next-to-last element in the array, and so on. For example:
arr = ['foo', 'bar', 42, :sym]
arr[-1] is the same as arr[3], arr[-2] is the same as arr[2], etc.

... it came back to the last position
When accessing elements, negative indices start counting from the end, i.e. ar[-1] is the last element.
... so the while doesn't break ever.
If Ruby did not allow negative indices, you would end up with an IndexError. Either way, you need j != 0 to ensure that your loop breaks when reaching the first element.
Here's another way to write it, using an upto and a downto loop:
def insertion_sort(a)
1.upto(a.size-1) do |i|
i.downto(1) do |j|
break unless a[j] < a[j-1]
a[j], a[j-1] = a[j-1], a[j]
end
end
end
array = [3, 7, 4, 9, 5, 2, 6, 1]
insertion_sort(array)
array #=> [1, 2, 3, 4, 5, 6, 7, 9]

Related

Quick Sort - How to apply recursive and get output

I am working on a school exercise on Quick Sort.
I have succeded to do the first exercise which is
Challenge
Given an array 'array' and a number 'p' in the first cell in the
array, can you partition the array so that all elements greater than
'p' is to the right of it and all the numbers smaller than 'p' are to
it's left? For example, if given the following as input:
4 5 3 9 1 The first number 4 is the pivot, so you should put the
smaller numbers to the left, and the larger to the right, and output:
3 1 4 5 9 The array should otherwise remain in the same order.
Can you write code to partition an array?
Example p partition([4, 5, 3, 9, 1])
=> [3, 1, 4, 5, 9]
My code for the above in ruby is
def partition(array)
# write your code here
pivot = array.shift()
base = [pivot]
left = []
right = []
array.each { |e| if e < pivot
left.push(e)
else
right.push(e)
end
}
left + base + right
end
p partition([4, 5, 3, 9, 1])
# => [3, 1, 4, 5, 9]
The Challenge for which I am raising this Question is
The function should output like this
p some_function_name([5, 8, 1, 3, 7, 10, 2])
# => 2 3
# 1 2 3
# 7 8 10
# 1 2 3 5 7 8 10
I am trying for the last 36hrs how to apply the partition code above recursively on this challenge. During my 36hrs of research on the Quick Sort algorithm, I can make the code to give the result of a sorted array, but this challenge is asking to provide prints at certain conditions which I am not able to achieve.
Any help is much appreciated.
This one tried for pivot at end
def partition(array)
# write your code here
pivot = array[-1]
i = -1
j = 0
while j < array.length-1
if array[j] < pivot
i += 1
array[i], array[j] = array[j], array[i]
end
j += 1
end
array.insert(i+1, array.pop)
puts index = i+1
puts (array.take index).join(' ')
puts (array.drop index+1).join(' ')
end
partition([5, 8, 1, 3, 7, 10, 2])
this one, I am not able to find a condition for terminating recursive function
def partition(array)
# write your code here
pivot = array.shift()
base = [pivot]
left = []
right = []
array.each { |e| if e < pivot
left.push(e)
else
right.push(e)
end
}
left + base + right
if left.length < 2
return
end
partition(left)
end
p partition([5, 8, 1, 3, 7, 10, 2])
p partition([1, 3, 2])
p partition([8, 7, 10])
It's not clear to my why you want partition to be recursive. There is no real natural way to make it recursive in a simple way. You can introduce a recursive helper method, but I don't see that as an improvement. partition really doesn't need to be more complicated than this:
def partition(array, pivot)
return [], [] if array.empty?
array.partition(&pivot.method(:>))
end
If you absolutely must, you can make it recursive like this:
def partition(...) = partition_rec(...)
private def partition_rec(array, pivot, left = [], right = [])
return left, right if array.empty?
first = array.first
rest = array.drop(1)
if first < pivot
partition_rec(rest, pivot, left + [first], right)
else
partition_rec(rest, pivot, left, right + [first])
end
end
With this partition in place, we can easily write our quicksort:
def quicksort(array)
return array if array.length < 2
pivot = array.first
left, right = partition(array.drop(1), pivot)
quicksort(left) + [pivot] + quicksort(right)
end
Now, all we need to do is to also print the result at each recursive call. A simple way to do that would be with Kernel#p, which returns its argument, so we can just insert it without changing the return value:
def quicksort(array)
return array if array.length < 2
pivot = array.first
left, right = partition(array.drop(1), pivot)
p quicksort(left) + [pivot] + quicksort(right)
end
If we need to replicate the exact format of the string as given in the question, then we should use Kernel#puts instead:
def quicksort(array)
return array if array.length < 2
pivot = array.first
left, right = partition(array.drop(1), pivot)
(quicksort(left) + [pivot] + quicksort(right)).tap do |result|
puts result.join(' ')
end
end
Note that there is a big no-no in your code. Here, you are modifying the argument passed into partition:
array.shift()
Same here:
array.insert(i+1, array.pop)
You must not, ever, mutate an argument. In fact, you should avoid mutation at all, as much as possible. The only thing you are allowed to mutate is yourself, i.e. self. But even then, you should be careful.

Quick Sort Algo

My algorithm is not working as intended. When I use a data set that has a starting value greater than the last element, the method sorts the numbers in descending order rather than ascending. I am not exactly sure changing the numbers at input[0] and input.length - 1 can alter the output from ascending to reverse order. I would appreciate any insight on how to fix this. Thanks!
def quickSort(input)
divide = lambda do |first, last|
if first >= last
return
end
mid = first
i = 0
while i < last do
if input[i] < input[last]
input[i], input[mid] = input[mid], input[i]
mid += 1
end
i += 1
end
input[mid], input[last] = input[last], input[mid]
divide.call(first, mid - 1)
divide.call(mid + 1, last)
end
divide.call(0, input.length - 1 )
return input
end
quickSort([24, 6, 8, 2, 35]) // causes a descending sort
quickSort([3,9,1,4,7]) // works as intended
I don't think that is quicksort (at least not the way I learned), and if you try adding more values to the first array you are sorting it will crash the program.
Take a look at this following implementation (my ruby is a bit rusty so bear with me)
def quickSort(input)
return input if input.length <= 1
i = input.length - 1
pivot = input[rand(i)]
input.delete(pivot)
lesser = []
greater = []
input.map do |n|
lesser.push(n) if n < pivot
greater.push(n) if n >= pivot
end
sorted = []
sorted.concat(quickSort(lesser))
sorted.push(pivot)
sorted.concat(quickSort(greater))
return sorted
end
print quickSort([24, 6, 8, 2, 35, 12])
puts ""
print quickSort([3,9,1,4,7,8,10,15,2])
puts ""
Usually when doing quicksort you will pick a random pivot in the array and split the array into parts lesser and greater than the pivot. Then you recursively call quicksort on the lesser and greater arrays before rejoining them into a sorted array. Hope that helps!

Ruby loops style/convention

I'm wondering how I would create the following psuedo-esque code's conventional equivalent in Ruby. I've read quite a bit regarding standard practices but I'm just curious what the generally accepted/cleanest approach to this specific loop structure might be. I haven't come across a direct example.
for(i = 0, j = 10; i < 10 && j > 0; i++, j--){
//do something
}
Side note:
The intended purpose for this loop structure is to iterate through an array of arrays diagonally, the loop will contain a number of break-ing conditions.
The intended purpose for this loop structure is to iterate through an array of arrays diagonally.
Given a square array of arrays:
array = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
]
You can traverse it diagonally (from top right to bottom left) via:
array.each.with_index(1) do |row, i|
puts row[-i]
end
Output:
3
5
7
Or, if you prefer (or need) an index-based approach:
0.upto(2).zip(2.downto(0)).each do |i, j|
p i: i, j: j, element: array[i][j]
end
Output:
{:i=>0, :j=>2, :element=>3}
{:i=>1, :j=>1, :element=>5}
{:i=>2, :j=>0, :element=>7}
You could also use array.each_index.zip(array.each_index.reverse_each)
You can do this
(0..10).each do |j|
i = 10 - j
...
end
# 0, 10 -> 10, 0
Not an exact equivalent, but the result is the same:
10.times do |i|
j = 10 - i
# do something
end
Or
10.times.lazy.map{|i| [i, 10 - i]}.each do |i, j|
# do something
end
Either way you have a cleaner solution than #gates' because they don't leak i and j out of the block.
Or you can try a more functional approach just for fun :)
(λ = ->(i, j) {
# do something
λ.(i + 1, j - 1) if i < 9 and j > 1
}).(0, 10)
But don't do this on large matrices, because Ruby doesn't optimize tail recursion thus may lead to stack overflow.
Try this
i = 0
j = 10
loop do
break if i > 9 || j < 1
# do something
i = i + 1
j = j - 1
end

implement shell sort by ruby

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]

Maximum and minimum value in an Array

I wrote a Ruby code to get max and min values from an array. The code prints the max value (8) correct but it's not printing the minimum value (2). Please let me know what went wrong in my code.
class MaxMinArray
def MaxMinMethod()
array = [4,2,8,3,5]
maxNo = array[0]
minNo = array[0]
arrayLength = array.length
for i in 1..arrayLength
if array[i].to_i > maxNo
maxNo = array[i]
end
if array[i].to_i < minNo
minNo = array[i]
end
end
puts "Maximum no. in the given array: " + maxNo.to_s
puts "Minimum no. in the given array: " + minNo.to_s
end
end
MaxiMinArrayObj = MaxMinArray.new
MaxiMinArrayObj.MaxMinMethod()
It is the combination of two things.
First, you iterated over for i in 1..arrayLength, which iterates past the last element in array. After the last element, array[i] is nil.
Second, you have the condition if array[i].to_i < minNo, which can be satisfied even if array[i] is not a number.
Because of that, the nil returned by array[i] after the last element satisfies the condition due to nil.to_i being 0, and that nil is assigned to minNo.
I realize you're trying to learn how to code, but, as you do so, it's also important to learn to take advantage of pre-existing solutions. Reinventing wheels will waste your time debugging code.
I'd write the code like:
def max_min(ary)
[ary.max, ary.min]
end
max_min([1,2,4]) # => [4, 1]
But, then again, Ruby already has a good minmax method:
[1,2,4].minmax # => [1, 4]
so use it and focus your energy on more interesting things.
If you have to see the values in the opposite order, use:
[1,2,4].minmax.reverse # => [4, 1]
A more verbose/old-school way of doing it is:
FIXNUM_MAX = (2 ** (0.size * 8 - 2) - 1)
FIXNUM_MIN = -(2 ** (0.size * 8 - 2))
def max_min(ary)
return [nil, nil] if ary.empty?
minval = FIXNUM_MAX
maxval = FIXNUM_MIN
ary.each do |i|
minval = i if i < minval
maxval = i if i > maxval
end
[maxval, minval]
end
max_min([1,2,4]) # => [4, 1]
[1,2,4].minmax.reverse # => [4, 1]
That simply loops over the array, checks each value to see if it's either smaller or larger than the last minimum or maximum value, and, if so, remembers it. Once the array is exhausted the values are returned. It's a lot more concise because using each removes a lot of the hassle of trying to walk the array using index values. We almost never use for in Ruby, especially to walk through an array.
(Technically Ruby can hold values well beyond 4611686018427387903 and -4611686018427387904, which are what FIXNUM_MAX and FIXNUM_MIN are, but those suffice for most things we want to do.)
It's not a good practice to print inside methods as long as you might want to use the results for something else.
Also Ruby comes with all sorts of magic methods to get the maximum and minimum of an array:
results = [5, 23, 43, 2, 3, 0].minmax
puts "Maximum no. in the given array: " + results[1]
puts "Minimum no. in the given array: " + results[0]
You should iterate from 1 to arrayLength - 1 (it's an index of the last element). You can use three dots for this:
for i in 1...arrayLength
If I were not allowed to used Ruby's minmax method, than I would do it probably like this:
array = [4,2,8,3,5]
min, max = nil, nil
array.each do |element|
min = element if min.nil? || element < min
max = element if max.nil? || max < element
end
puts [min, max]
# => [2, 8]
I used this expression for the min and max within ruby, it's a stretch but it works
class RubyMinMax
def self.min_function(array=[])
puts "my array is the following #{array}"
puts "the length of the array is #{array.length}"
it = 0
while array.length > 1
array.fetch(it).to_i > array.fetch(it-1).to_i ? array.delete_at(it) : array.delete_at(it-1)
it = array.length-1
end
print array[0]
end
def self.max_function(array=[])
puts "my array is the following #{array}"
puts "the length of the array is #{array.length}"
it = 0
while array.length > 1
array.fetch(it).to_i < array.fetch(it-1).to_i ? array.delete_at(it) : array.delete_at(it-1)
it = array.length-1
end
print array[0]
end
end
RubyMinMax.min_function([18, 19, 17])
RubyMinMax.max_function([18, 19, 17])
In the simplest way you can use max and min method of array.
:001 > [1,4,1,3,4,5].max
=> 5
:002 > [1,4,1,3,4,5].min
=> 1
And if your array may contain nil the first compact it the use min max
For example
:003 > [1,4,1,3,4,5,nil].compact
=> [1, 4, 1, 3, 4, 5]
:004 > [1,4,1,3,4,5].max
=> 5
:005 > [1,4,1,3,4,5].min
=> 1

Resources