recursive binary search in ruby - ruby

I've been learning some algorithms and I can't find the reason why my method is failing. if you could look at the code and shed some light as to why that is happening. I would truly appreciate it.
I'm trying to write a method that would binary search an array recursively and so far that is all my code.
def recursive_binary_search(arr, target)
max_index = arr.length - 1
mid_index = max_index / 2
if arr[mid_index] > target
new_arr = arr[0..(mid_index - 1)]
recursive_binary_search(new_arr, target)
elsif arr[mid_index] < target
new_arr = arr[(mid_index + 1)..max_index]
recursive_binary_search(new_arr, target)
else
return mid_index
end
end
The error I keep getting is undefined method '>' for nil:NilClass

I was unable to reproduce the exception reported by the OP (as the data that produced the exception was not given in the question), but the main problem is that, because max_index is computed from arr, and arr is constantly getting smaller, the index returned by the method will have no relation to the correct index in the initial array arr.
Suppose, for example, that arr = [1,2,3,4,5,6] and target = 6. In this case the method will return 0 (rather than 5) as the index of the target element. That's because arr will progressively become arr[3..6], arr[4..6], arr[5..6] and arr[6], at which point index 0 will be returned.
Here is one way the method could be written, using a case statement. The method assumes that target is an element of arr and (as required by binary searches) the elements of arr are ordered, smallest to largest.
def recursive_binary_search(arr, target, min_index=0, max_index=arr.size-1)
mid_index = (min_index+max_index)/2
case arr[mid_index] <=> target
when 0 # arr[mid_index] == target
mid_index
when -1 # arr[mid_index] < target
min_index = mid_index + 1
recursive_binary_search(arr, target, min_index, max_index)
when 1 # arr[mid_index] > target
max_index = mid_index - 1
recursive_binary_search(arr, target, min_index, max_index)
end
end
arr = [1,2,3,4,5,6]
arr.each { |target| puts "#{target}: #{recursive_binary_search(arr, target)}" }
1: 0
2: 1
3: 2
4: 3
5: 4
6: 5

If your arrays are sorted you could try something like this:
def search(arr, target)
return nil if array.empty?
mid_index = array.length / 2
case target <=> array[mid_index]
when -1
search(array.take(mid_index), target)
when 0
mid_index
when 1
subs = search(array.drop(mid_index + 1), target)
subs.nil? ? nil : (mid_index + 1) + subs
end
end

Related

undefined method `<' for nil:NilClass error but no nil exists?

Was wondering why I get the error: "undefined method `<' for nil:NilClass" when compiling. After looking for reasons why, I found that you cannot use [] on an object with nil as a value. This makes sense, but I don't see why my array would contain nil in it. What am I missing?
def binary_search(n, arr)
middle = arr.length #pick middle value
i = 0
j = arr.length - 1
while i <= j
if arr[middle] == n
return true
elsif arr[middle] < n
i = middle + 1
middle = (i + j) / 2
else
j = middle - 1
middle = (i + j) / 2
end
end
false
end
nums = [76,32,50,90,10,8,15,49]
nums.sort
puts nums.inspect
binary_search(50, nums)
Let's look at a simplified subset of the code:
arr = [76,32,50,90,10,8,15,49]
middle = arr.length # 8
arr[middle] < 50 # NoMethodError
The length is 8.
arr[8] is nil, because there is no item at index 8. Remember that Ruby indexes begin with 0.
nil < 50 is a NoMethodError

How to loop over an entire method until you achieve what you want ? (ruby)

I'm learning ruby and practicing with codewars, and I've come to a challenge that I feel I mainly understand (rudimentarily) but I'm unable to figure out how to continue looping over the method until I reach the result I'm looking for.
The challenge is asking to reduce a number, by multiplying its digits, until the multiplication results in a single digit. In the end it wants you to return the number of times you had to multiply the number until you arrived at a single digit. Example -> given -> 39; 3 * 9 = 27, 2 * 7 = 14, 1 * 4 = 4; answer -> 3
Here's my code :
def persistence(n)
if n < 10
return 0
end
arr = n.to_s.split("")
sum = 1
count = 0
arr.each do |num|
sum *= num.to_i
if num == arr[-1]
count += 1
end
end
if sum < 10
return count
else
persistence(sum)
end
end
Thanks for your help!
Your function is looking great with recursion but you are reseting the count variable to 0 each time the loop runs, I think if you use an auxiliar method it should run ok:
this is in base of your code with minor improvements:
def persistence(n)
return 0 if n < 10
count = 0
multiply_values(n, count)
end
def multiply_values(n, count)
arr = n.to_s.chars
sum = 1
arr.each do |num|
sum *= num.to_i
if num == arr[-1]
count += 1
end
end
if sum < 10
return count
else
multiply_values(sum, count)
end
end
a shorter solution could be to do:
def persistence(n)
return 0 if n < 10
multiply_values(n, 1)
end
def multiply_values(n, count)
sum = n.to_s.chars.map(&:to_i).reduce(&:*)
return count if sum < 10
multiply_values(sum, count + 1)
end
and without recursion:
def persistence(n)
return 0 if n < 10
count = 0
while n > 10
n = n.to_s.chars.map(&:to_i).reduce(&:*)
count += 1
end
count
end
Let's look at a nicer way to do this once:
num = 1234
product = num.to_s.split("").map(&:to_i).reduce(&:*)
Breaking it down:
num.to_s.split("")
As you know, this gets us ["1", "2", "3", "4"]. We can easily get back to [1, 2, 3, 4] by mapping the #to_i method to each string in that array.
num.to_s.split("").map(&:to_i)
We then need to multiply them together. #reduce is a handy method. We can pass it a block:
num.to_s.split("").map(&:to_i).reduce { |a, b| a * b }
Or take a shortcut:
num.to_s.split("").map(&:to_i).reduce(&:*)
As for looping, you could employ recursion, and create product_of_digits as a new method for Integer.
class Integer
def product_of_digits
if self < 10
self
else
self.to_s.split("").map(&:to_i).reduce(&:*).product_of_digits
end
end
end
We can now simply call this method on any integer.
1344.product_of_digits # => 6

Ruby: undefined method '>' for nil:NilClass (NoMethodError)

Currently working on a binary search algorithm for Ruby, but am running into an error when comparing the middle array element with n.
Code Below:
def b_search(n, arr)
middle = arr.length / 2
first = 0
last = arr.length - 1
while first <= last
middle = first + last / 2
if arr[middle] == n
return true
elsif arr[middle] > n
last = middle - 1
else
first = middle + 1
end
end
false
end
nums = [1, 2, 3, 4, 5]
target = 4
if b_search(target, nums)
puts "Target Found"
else
puts "Target Not Found"
end
The error evidently happens in the while loop at the
elsif arr[middle] > n
line, but I'm not sure why. Any help would be appreciated.
It occurs because arr[middle] is nil and nil has no method > defined on it.
Consider that
middle = first + last / 2
is equals to
middle = first + (last / 2)
and you probably meant
middle = (first + last) / 2
so use the parentheses because / has a higher precedence

Ruby undefined Method/NoMethodError '*' for nil:NilClass, works until looped

My program orders arrays 1 and 2, and iterates to check whether each element in 1 is the sqrt of each element in 2. I've tested the comparison without the loop and it works fine, so I don't think that there's an uninitialised variable.
def comp(array1, array2)
order1 = array1.sort
order2 = array2.sort
i = 0
while i < order1.length
if order1[i] * order1[i] == order2[i]
i += 1
else
false
end
end
order1[i] * order1[i] == order2[i]
end
Can you point me in the direction of the issue? I've also not used Math.sqrt because it times out on my interface.
Your i equals order1.length, after your loop, so the last line of your method is basically
order1[order1.length] * order1[order1.length] == order2[order1.length]
which is (assuming your arrays are the same length):
nil * nil == nil
which throws an error. Not sure why you need the last line, if you remove it and simply return a counter, your method works as expected if you use a dedicated counter, for the elements which match your condition, instead of using index for that (your index has to be incremented always):
def comp(array1, array2)
order1 = array1.sort
order2 = array2.sort
i = 0
counter = 0
while i < order1.length
if order1[i] * order1[i] == order2[i]
counter += 1
end
i += 1
end
counter
end
In Ruby it is pretty common to use proper enumerators for iterating over collections, so your while can be nicely substituted by Enumerable#each_with_index:
def comp(array1, array2)
order1 = array1.sort
order2 = array2.sort
counter = 0
order1.each_with_index do |el, i|
if el * el == order2[i]
counter += 1
end
end
counter
end
And as the last step, we can also Array#count how many elements in an array meet a certain condition without needing to specify a local variable, like so:
def comp(array1, array2)
order2 = array2.sort
array1.sort.each_with_index.count { |el, i| el ** 2 == order2[i] }
end

NoMethodError with .chr.to_i

I'm trying to create a recursive method sum_of_digits(i) that takes the sum of integers, i.e. '456' = 4+5+6 = 15
However, I receive a NoMethodError for chr.to_i in the following code:
def sum_of_digits(i)
input = i.to_s
if i == 0
return 0
elsif input.length == 1
return i
else
for n in 1..input.length
sum += input[i].chr.to_i % 10^(n-1)
end
end
return sum
end
Thank you!
String indexes are zero-based in ruby. The problem is here:
for n in 1..input.length
it should be written as
for n in 0..input.length-1
BTW, call to chr is superfluous as well, since you already have a string representation of a digit there. As well, sum must be declared in advance and set to zero.
Also, the whole code is not ruby idiomatic: one should avoid using unnecessary returns and for-loop. The modified version (just in case) would be:
def sum_of_digits(i)
input = i.to_s
case
when i == 0 then 0 # return zero
when input.length == 1 then i # return i
else
sum = 0
input.length.times do |index|
sum += input[index].to_i % 10^index
end
sum
end
end
or, even better, instead of
sum = 0
input.length.times do |index|
sum += input[index].to_i % 10^index
end
sum
one might use inject:
input.length.times.inject(0) do |sum, index|
sum += input[index].to_i % 10^index
end

Resources