Merge sort infinite recursion - ruby

I haven't started the merge process yet. So far I've only focused on dividing the array over and over again until only 1 number is left in the array. Below is my code showing how I implemented this so far.
def mergesort(list)
mid = list.length / 2
left = list[0, mid]
right = list[mid, list.size]
until left.size <= 1 || right.size <= 1 do
test(mergesort(left), mergesort(right))
end
print left
print right
def test(left, right)
sorted = []
left.length / 2
right.length / 2
# print sorted
end
end
My problem is the loop will not break. I'm hoping someone can point me in the right direction. I'm also open to resources to help me solve this problem better. I would like some guidance in solving this issue that has arise.

Your looping is infinite. left and right are not changed later in a deeper step so they both remain bigger than 1.
until left.size <= 1 || right.size <= 1 do
test(mergesort(left), mergesort(right))
end
How to fix this
One of the concepts that merge-sort uses is recursion
Those sorting algorithms use the stack instead, because it should work for any size. You should implement your end condition in your recursive method. For example:
def mergesort(list)
return list if array.size <= 1 # end condition
mid = list.length / 2
left = mergesort list[0, mid]
right = mergesort list[mid, list.size]
print left
print right
# TODO merge left and right
end
As you can see we first do a merge sort on the first half recursively until the size of the array is smaller or equal to 1. Followed by the right part.

Related

How can I further code my merged_sort method in ruby?

So this one is really tough since it uses recursion and I cant go anymore further than this. I dont know what to do next.
for description, merge_sort divides the array until it has been divided into single elements and then I dont know what to do next its really hard I have been thinking this through since 8 hours
def merge_sort ( array )
return if array.length < 2
a = merge_sort(array.slice!(0..array.length/2))
b = merge_sort(array)
end
def merge ( a , b )
merged = []
j_a = 0 # pointer to the first list
k_b = 0 # pointer to the second list
while j_a < a.length || k_b < b.length
if a[j_a] > b[k_b]
merged << b[k_b]
k_b += 1
else
merged << a[j_a]
j_a += 1
end
if j_a == a.length # pointer has reached the end of first list? append the whole of 2nd `list`
merged << b[k_b..-1]
else
merged << a[j_a..-1] # else append the first list to merged.
end
end
merged
end
Merge sort has two phases, (1) divide, and (2) conquer/merge.
The divide phase splits an array into left and right halves.
The merge phase merges the results.
Your merge_sort() function needs to be called recursively on the left half and the right half, each returning a sorted subarray.
Then the merge function needs to be called to merge the two subarrays.
Basically, when you want to sort an array, the recursive function divides array in half during recursion, then merge sorted halves as recursion unwinds, something like this,
# merge sort array charles
def merge_sort ( charles )
return if charles.length < 2
k = charles.length
L = merge_sort(charles[0:k/2])
R = merge_sort(charles[k/2+1:k])
# missing merge
return merge(L,R)
end

Stack to deep using Merge Sort

I'm trying to implement merge sort on my own using recursion.
def merge_sort(a,i,j)
if i < j
merge_sort(a,i,j/2)
merge_sort(a,j/2+1,j)
merge(a,i,j/2,j/2+1,j)
end
end
def merge(a,i,j,k,l)
# No implementation yet
end
The problem is my implementation results in the stack going too deep. I shouldn't be getting this error message for such a small array. The array I'm trying to sort is just five elements.
b = [5,4,3,2,1]
p merge_sort(b,0,b.size - 1) # => results in 'stack to deep' message
Here's a step in the right direction that's made a bit more Ruby-like in how it's more forgiving, plus as a bonus has actual names instead of mathematical shorthand:
def merge_sort(arr,from = nil,to = nil)
from ||= 0
to ||= arr.length
if (from < to)
part = from + (to - from) / 2
merge_sort(arr, from, part)
merge_sort(arr, part + 1, to)
merge(arr, from,part, part+1, to)
end
end
def merge(a,from,j,k,l)
# No implementation yet
end
b = [5,4,3,2,1]
merge_sort(b)
The mistake came about from not properly defining the partition point. In the original code for an array of length 5 the cut point would be 2, and when that's further divided, the cut point is 2/1 or 1, not 2+(5-3)/2 or 3 as it should be. From there it all went crazy because it was doing the math wrong and kept calling itself for no reason.
My problem was my mid-point formula was off throwing the recursion into an infinite loop recursing until the stack overflowed. Instead of
j / 2 + 1
It should have been
(i+j) / 2 + 1
tadman got the formula right in his re-versioning

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]

Ruby challenge: experienced developers opinions

I'm working on some ruby problems geared towards new developers, but I would like the opinions of experienced developers on this. Sorry for the long post, and I really appreciate your time and opinions.
Problem Question
Write a function, nearest_larger(arr, i) which takes an array and an
index. The function should return another index, j: this should
satisfy:
(a) arr[i] < arr[j], AND
(b) there is no j2 closer to i than j where arr[i] < arr[j].
In case of ties (see example below), choose the earliest (left-most)
of the two indices. If no number in arr is larger than arr[i],
return nil.
Difficulty: 2/5
Rspec Test
describe "#nearest_larger" do
it "handles a simple case to the right" do
nearest_larger([2,3,4,8], 2).should == 3
end
it "handles a simple case to the left" do
nearest_larger([2,8,4,3], 2).should == 1
end
it "treats any two larger numbers like a tie" do
nearest_larger([2,6,4,8], 2).should == 1
end
it "should choose the left case in a tie" do
nearest_larger([2,6,4,6], 2).should == 1
end
it "handles a case with an answer > 1 distance to the left" do
nearest_larger([8,2,4,3], 2).should == 0
end
it "handles a case with an answer > 1 distance to the right" do
nearest_larger([2,4,3,8], 1).should == 3
end
it "should return nil if no larger number is found" do
nearest_larger( [2, 6, 4, 8], 3).should == nil
end
end
Solution
def nearest_larger arr, idx
diff = 1
loop do
l = idx - diff
r = idx + diff
return l if (l >= 0) && (arr[l] > arr[idx])
return r if (r < arr.length) && (arr[r] > arr[idx])
return nil if (l < 0) && (r >= arr.length)
diff += 1
end
end
Feedback
How would you go about working towards a solution for this problem? (what's your process?)
In your opinion do find the Problem Question clear and easy to understand?
How long should it take you to solve this problem? (10min, 20min, ...?)
Do agree with the level of difficulty? (Keep in mind this is geared towards new developers)
If willing: please post your own solution, showcasing your style of solving this problem.
I decided to post this question because I know how easy it can be for new developer to get stuck on a problem and not know what to write first. I'm hoping your responses will give an insight on how you would work through a problem that you perceive as a challenge.
I have not an experienced developer, or even an inexperienced one, but I will give you my thoughts anyway.
1 How would you go about working towards a solution for this problem? (what's your process?)
I would look to break into pieces, but surely everyone does that. For example, here the values in the array are only used to pull out the indices of elements that are larger, so I'd see the first problem as pulling out the indices and the second problem as dealing with the indices alone. I'd further simplify the latter by subtracting i from each index so that j and be compared to k like so: if j.abs < k.abs ..., rather than if (j-i).abs < (k-i).abs.... In choosing among different approaches, I tend to look for the one that is most easily understood ("reads best").
2. In your opinion do find the Problem Question clear and easy to understand?
Yes.
3. How long should it take you to solve this problem?
I refuse to answer on the grounds that it would surely incriminate me.
4. Do you agree with the level of difficulty?
It seems about right. It would be a "beginner" problem at rubeque.com.
5. If willing: please post your own solution, showcasing your style of solving this problem.
Sure.
def nearest_larger(arr, i)
ret = nearest_to_zero( arr.each_with_index
.select { |e,j| e > arr[i] }
.map { |_,j| j-i } )
ret ? ret + i : nil
end
I looked at two ways of writing nearest_to_zero(). The first is short, direct and clear, but inefficient, using sort!:
def nearest_to_zero(a)
a.sort! { |j,k| (j.abs == k.abs) ? j <=> k : j.abs <=> k.abs }
a.any? ? a.first : nil
end
More efficient, but not as pretty:
def nearest_to_zero(a)
neg, pos = a.partition { |e| e < 0 }
case
when neg.empty?
pos.empty? ? nil : pos.first
when pos.empty?
neg.last
else
pos.last.abs < neg.last.abs ? pos.first : neg.last
end
end
For arr = [2,5,4,8,10], i = 2, the following steps are performed by nearest_larger():
a = arr.each_with_index.select { |e,j| e > arr[i] } # => [[5,1],[8,3],[10,4]]
b = a.map { |_,j| j-i } # => [-1,1,2]
ret = nearest_to_zero(b) # => -1
ret ? ret + i : nil # => 1
In the first nearest_to_zero(), if two indices have equal absolute value (meaning they are equally close to i before the transformation), the tie goes to the index with the lower vlaue; else it is the index with the smaller absolute value.
In the second nearest_to_zero():
neg, pos = [-1,1,2].partition {|e| e < 0} # => [[-1],[1,2]]
The rest should be self-explanatory.
I had read about rspec, but had not used it before. It was about time that it did. My code passed.
How would you go about working towards a solution for this problem? (what's your process?)
Start with a simple example, e.g. one of the tests. It is discovered that if the array element arr[i-1] is greater than arr[i] then you can immediately return i-1 as the answer. So you can just check in succession: i-1, i+1, i-2, i+2, i-3, i+3 etc. and return the first index that satisfies the inequality.
In your opinion do find the Problem Question clear and easy to understand?
Yes; the tests help but it only confirmed my understanding from the worded problem.
How long should it take you to solve this problem? (10min, 20min, ...?)
For a student in a test/classroom environment, no more than 10min. Depending on how much preparatory material they have had before this, maybe even less.
Do agree with the level of difficulty? (Keep in mind this is geared towards new developers)
Yes, 2/5 seems right.
If willing: please post your own solution, showcasing your style of solving this problem.
def nearest_larger( a, i )
2.upto([i,a.length-i].max << 1) do |k|
j = (k&1).zero? ? i - (k>>1) : i + (k>>1)
return j if 0 <= j && j < a.length && a[i] < a[j]
end
return nil
end
Addendum: Thinking in Bits
This addendum will go through in greater detail the problem solving that went into the above solution for the benefit of new programmers.
As was mentioned in the answer to Question #1 above, the return value of nearest_larger is the first index j for which a[i] < a[j] as j iterates through the sequence
i-1, i+1, i-2, i+2, i-3, i+3, ...
This opens the way to a sub-problem, which is how to generate this sequence of numbers. When actually writing the program, I used comments as a "scratch pad", and in the code had something like this:
# -1, 1, -2, 2, -3, 3, ... (Sequence G)
from which the prior sequence is constructed by just adding i to each term. Call this Sequence G. Now this is where a "binary intuition" would come into play. Consider a simple sequence of binary numbers that increases by one after each term, shown in Column A, and the familiar decimal representation is shown in Column B:
A B C D E F
----------------------------
0000 0 000 0 0 0
0001 1 000 1 0 0
0010 2 001 0 1 -1
0011 3 001 1 1 1
0100 4 010 0 2 -2
0101 5 010 1 2 2
0110 6 011 0 3 -3
0111 7 011 1 3 3
Now split the bits in each number into two groups: all the bits other than bit 0 (the right-most bit) as shown in Column C, and bit 0 shown in Column D. In other words, concatenate C and D to get A. The decimal representation of C is in column E. Notice that column D conveniently flips between 0 and 1, just as in Sequence G the numbers flip between negative and positive. We will use this to construct column F, which is the same as E, except when D is 0 make F negative. Finally, if we just start in the above table at A=0010 (or B=2) then Column F gives us the above Sequence G.
So now how do we get Column F from A in code? This is where bit operations come in to play.
C = A >> 1 - The >> right-shift operator shifts the bits on the LHS (left-hand side) by RHS (right-hand side). In this case, each value A is shifted to the right one place. The right-most bit is lost. Mathematically, it is the same as dividing by 2 and dropping the remainder in this case (B/2 == E with remainder dropped.)
D = A & 1 - The & is the bitwise AND operator. By "anding" A with 1, we select only bit 0; see the link in the prior sentence for more detail. This gives us Column D.
Putting this together in the code, we'll have k be the iteration variable that starts at 2 and increments by 1 each time. Then the above analysis gives us j:
j = (k&1).zero? ? i - (k>>1) : i + (k>>1)
The first value for j which is both in bounds and for which a[i] < a[j] holds is automatically the answer, so it can be returned immediately:
return j if 0 <= j && j < a.length && a[i] < a[j]
Finally, if there are no valid values for j then return nil. Other than calculating a lower upper-bound for k, which is left as a homework problem, that is the entirety of the nearest_larger function.
In actual practice, for a problem like this, a readable solution as posed in the OP is preferable since it is more clear and accessible to a wider group of programmers. This present approach was motivated by an opportunity to demonstrate the use of bit operations.

Simple recursion problem

I'm taking my first steps into recursion and dynamic programming and have a question about forming subproblems to model the recursion.
Problem:
How many different ways are there to
flip a fair coin 5 times and not have
three or more heads in a row?
If some could put up some heavily commented code (Ruby preferred but not essential) to help me get there. I am not a student if that matters, this is a modification of a Project Euler problem to make it very simple for me to grasp. I just need to get the hang of writing recursion formulas.
If you would like to abstract the problem into how many different ways are there to flip a fair coin Y times and not have Z or more heads in a row, that may be beneficial as well. Thanks again, this website rocks.
You can simply create a formula for that:
The number of ways to flip a coin 5 times without having 3 heads in a row is equal to the number of combinations of 5 coin flips minus the combinations with at least three heads in a row. In this case:
HHH-- (4 combinations)
THHH- (2 combinations)
TTHHH (1 combination)
The total number of combinations = 2^5 = 32. And 32 - 7 = 25.
If we flip a coin N times without Q heads in a row, the total amount is 2^N and the amount with at least Q heads is 2^(N-Q+1)-1. So the general answer is:
Flip(N,Q) = 2^N - 2^(N-Q+1) +1
Of course you can use recursion to simulate the total amount:
flipme: N x N -> N
flipme(flipsleft, maxhead) = flip(flipsleft, maxhead, 0)
flip: N x N x N -> N
flip(flipsleft, maxhead, headcount) ==
if flipsleft <= 0 then 0
else if maxhead<=headcount then 0
else
flip(flipsleft - 1, maxhead, headcount+1) + // head
flip(flipsleft - 1, maxhead, maxhead) // tail
Here's my solution in Ruby
def combination(length=5)
return [[]] if length == 0
combination(length-1).collect {|c| [:h] + c if c[0..1]!= [:h,:h]}.compact +
combination(length-1).collect {|c| [:t] + c }
end
puts "There are #{combination.length} ways"
All recursive methods start with an early out for the end case.
return [[]] if length == 0
This returns an array of combinations, where the only combination of zero length is []
The next bit (simplified) is...
combination(length-1).collect {|c| [:h] + c } +
combination(length-1).collect {|c| [:t] + c }
So.. this says.. I want all combinations that are one shorter than the desired length with a :head added to each of them... plus all the combinations that are one shorter with a tail added to them.
The way to think about recursion is.. "assuming I had a method to do the n-1 case.. what would I have to add to make it cover the n case". To me it feels like proof by induction.
This code would generate all combinations of heads and tails up to the given length.
We don't want ones that have :h :h :h. That can only happen where we have :h :h and we are adding a :h. So... I put an if c[0..1] != [:h,:h] on the adding of the :h so it will return nil instead of an array when it was about to make an invalid combination.
I then had to compact the result to ignore all results that are just nil
Isn't this a matter of taking all possible 5 bit sequences and removing the cases where there are three sequential 1 bits (assuming 1 = heads, 0 = tails)?
Here's one way to do it in Python:
#This will hold all possible combinations of flipping the coins.
flips = [[]]
for i in range(5):
#Loop through the existing permutations, and add either 'h' or 't'
#to the end.
for j in range(len(flips)):
f = flips[j]
tails = list(f)
tails.append('t')
flips.append(tails)
f.append('h')
#Now count how many of the permutations match our criteria.
fewEnoughHeadsCount = 0
for flip in flips:
hCount = 0
hasTooManyHeads = False
for c in flip:
if c == 'h': hCount += 1
else: hCount = 0
if hCount >= 3: hasTooManyHeads = True
if not hasTooManyHeads: fewEnoughHeadsCount += 1
print 'There are %s ways.' % fewEnoughHeadsCount
This breaks down to:
How many ways are there to flip a fair coin four times when the first flip was heads + when the first flip was tails:
So in python:
HEADS = "1"
TAILS = "0"
def threeOrMoreHeadsInARow(bits):
return "111" in bits
def flip(n = 5, flips = ""):
if threeOrMoreHeadsInARow(flips):
return 0
if n == 0:
return 1
return flip(n - 1, flips + HEADS) + flip(n - 1, flips + TAILS)
Here's a recursive combination function using Ruby yield statements:
def combinations(values, n)
if n.zero?
yield []
else
combinations(values, n - 1) do |combo_tail|
values.each do |value|
yield [value] + combo_tail
end
end
end
end
And you could use regular expressions to parse out three heads in a row:
def three_heads_in_a_row(s)
([/hhh../, /.hhh./, /..hhh/].collect {|pat| pat.match(s)}).any?
end
Finally, you would get the answer using something like this:
total_count = 0
filter_count = 0
combinations(["h", "t"], 5) do |combo|
count += 1
unless three_heads_in_a_row(combo.join)
filter_count += 1
end
end
puts "TOTAL: #{ total_count }"
puts "FILTERED: #{ filter_count }"
So that's how I would do it :)

Resources