reversed expression in bsearch block not working - ruby

ary = [1, 4, 6, 9]
(0...ary.size).bsearch { |i|
ary[i] - 1
} # => nil
1 - ary[i] # => 0
When the code is written in a form ary[i] - 1 which doesn't work as expected.
What I am trying to do is to find the index of the number 1 in the array.
But 1 - ary[i] can return the number's index correctly. Why doesn't ary[i] - 1 work?

Array#bsearch is meant to perform binary search to find an element that meets certain criteria. As per documentation, if you return numeric values from the block, the find-any mode type of search is used.
The search starts at center of the sorted array - and if block returns negative value, it continues search in first half, and if block returns positive value, it continues the search in second half of the array.
In your case when you use ary[i] - 1, the value returned by block is always positive and search continues recursively on second half of the array - and never finds the value 1.
Here is the code with some debug statements:
ary = [1, 4, 6, 9]
p (0...ary.size).bsearch { |i|
puts "Elem: #{ary[i]} Index: #{i}"
ary[i] - 1
}
Output:
Elem: 4 Index: 1
Elem: 6 Index: 2
Elem: 9 Index: 3
nil
[Finished in 0.4s]

Array#bsearch returns an element of the array, not the index of a matching element.
You might want to use Array#index instead.

If you want to find the index of the element instead of the element itself, you need to use Array#bsearch_index. Note: this method was introduced in Ruby 2.3, which at the time of this writing has not been released yet (it will be released on Christmas 2015).
The feature request for Array#bsearch_index contains a comment by Yusuke Endoh showing how to implement Array#bsearch_index (and in fact Array#bsearch as well) based on Range#bsearch:
class Array
def bsearch_index(&blk)
return enum_for(__method__) unless blk
(0...size).bsearch {|i| yield self[i] }
end
end
When either running Ruby 2.3 or using the above monkey patch, you can then do:
ary.bsearch_index(&1.method(:-))
in order to find the index of the 1 element in your array.
The reason why it doesn't work with
ary.bsearch_index {|el | el - 1 }
is simple: the block violates the contract of bsearch_index (and also bsearch since they are the same). The block needs to return a positive number for indices left of the one you are searching for, a negative number for indices right of the one you are searching for, and 0 for indices within the range you are searching for. Your block does the opposite.

Related

How to solve a recursive Backtracking problem

I have a recursive Backtracking problem for school, and I do not understand how I would go about solving it.
Given an array of integers, determine if it is possible to choose a group of those integers that add to a particular sum. Use a recursive method called sum_to_total to solve the problem (no loops!).
Examples:
"Array [3, 6, 7]" and "Sum 10" returns true, since 3 + 7 = 10
"Array [1, 2, 3]" and "Sum 6" returns true, since 1 + 2 + 3 = 6
"Array [2, 4, 6]" and "sum 5" returns false, since no combination of these numbers sum to 5
This is what I have got so far:
def self.sum_to_total(sum, array, index)
##sum_num += array[index]
return false if ##sum_num > sum || ##sum_num < sum
return false if index<board.length || index<0
return true if ##sum_num == sum
return solvable(sum, array, index+1)
end
##sum_num = 0
puts sum_to_total(10, [3, 5, 7], 0)
A few pointers would help.
You've taken a shot at this, so here are some pointers that should help you move forward.
By "recursive" we mean a method that calls itself. Your solvable needs to be a call to sum_to_total.
A recursive method needs to pass in any values that change during the method, if you have to access the updated values on the next call. So, index as you have it is correct, but you also have to pass in sum_num.
You can use defaults to initialize your index and sum_num on the first call. You don't need to mess with global variables. (And shouldn't. Global variables are evil. Most of the time.)
You only want to return when you have gone through the entire array. You do not use return on your recursive method call, you just call the method.
You don't need to make this a class method by using self.method_name.
I'll show you the basic recursive method, and leave you to work out the (more difficult) backtracking requirement. (This basic method will solve for the case of whether the entire array adds up to the sum. The backtracking part is necessary to determine whether or not a subset of the array does.)
def sum_to_total(sum, array, index = 0, sum_num = 0)
sum_num += array[index]
return sum_num == sum if index == array.size - 1
sum_to_total(sum, array, index + 1, sum_num)
end
The three lines of code do this:
Add the current array value to the sum. (You pretty much had this one ok.)
If you're done going through the array, return whether or not the sum of the array equals the provided sum value.
(If you aren't done going through the array) call the method again, passing in the incremented index value and the value of the accumulated sum so far. (You were on the right track!)
Here's an article that should help you with the backtracking part.

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

Ruby Exercise: Count the numbers in an array between a given range

So working through the above exercise and found this solution on GitHub.
def count_between arr, lower, upper
return 0 if arr.length == 0 || lower > upper
return arr.length if lower == upper
range = (lower..upper).to_a
arr.select { |value| range.include?(value) }.length
end
I understand what the first three lines mean and why they return the values they do. What I'd like to understand are the following lines of code.
Line 4 (below) is defining "range" as a variable and uses the lower...upper as the range variables (just discovered you don't need to put an integer value in a range. What does '.to_a' mean, can't seem to find it in the ruby docs, and what does it do?
range = (lower..upper).to_a
Line 5 (below) is using an Array#select method and its saying select this value if the value is included in this range and then give me the Array#length of all selected values, but I don't quite understand A. what |value| is doing and what it means. B. range.include?(value) means is this value included in this range I am assuming.
arr.select { |value| range.include?(value) }.length
Actually, I'd simplify to this:
def count_between arr, lower, upper
return 0 if lower > upper
arr.count{|v| (lower..upper).include?(v)}
end
to_a is documented here; it returns an Array containing each element in the Range. However, there's no reason to call to_a on the Range before calling include?.
There's also no reason to special-case the empty array.
Returning the length of the array when lower equals upper makes no sense.
value is the name given to the value the block is called with. I think a simple v is better for such a trivial case.
select calls the block for each value in arr and returns a new Array containing the elements for which the block returns true, so the length of that new Array is the number of matching values. However, count exists, and makes more sense to use, since the count is all we care about.
Update: As #steenslag points out in the comments, Comparable#between? can be used instead of creating a Range on which to call include?, and this eliminates the need to ensure that lower is less than or equal to upper:
def count_between arr, lower, upper
arr.count{|v| v.between?(lower, upper)}
end
to_a means convert to array
irb(main):001:0> (1..5).to_a
=> [1, 2, 3, 4, 5]
select method passes each element to the block and Returns a new array containing all elements of ary for which the given block returns a true value.. In your case it simply checks if the value is contained in the range array. range is an array not a range.
## if arr is [1,5] for eg:
irb(main):005:0> [1,5].select {|value| range.include?(value)}
=> [1, 5]
irb(main):006:0> [1,5].select {|value| range.include?(value)}.length
=> 2
so the elements of arr are contained in the |value| variable inside the block.
It's a block.
As the documentation says: select "Returns a new array containing all elements of ary for which the given block returns a true value."
So for each object in arr it is passed to the block in which you provide whatever code you want to that returns true or false, and the select statement uses this result to add the value to the the array that it returns. And after that, length is called on the array.
So you have an array, you filter the array to contain only the numbers that are in the range, and then you take the length - effectively counting the number of elements.

How does negative index work with `Array#[]=`?

I tried to see how Array#[]= works, and played around:
enum[int] = obj → obj
enum[start, length] = obj → obj
enum[range] = obj → obj
Question 1
I have one array b holding nil at its 0 index.
b = []
b[0] # => nil
I tried to replace nil with integer 10 in the code below.
b[-1] = 10 # => IndexError: index -1 too small for array; minimum: 0
Why doesn't the code above work, but the ones below do? In case of an array with size 1, why are the indices 0 and -1 treated differently?
b[0] = 5 # => 5
b[-1] = 10 # => 10
Question 2
I created an array of size 2, and did the following:
a = [1,2]
a[-3] = 3 # => IndexError: index -3 too small for array; minimum: -2
a[-3] = [3] # => IndexError: index -3 too small for array; minimum: -2
a[-3..-4] = [3] # => RangeError: -3..-4 out of range
I believe that negative index never increases the size of an array, but I don't know why. Why did the code below succeed?
a[-2..-3] = [3,4] #=> [3, 4]
I would suggest you to take a look at the first para in Array documentation. It surprisingly says: “A negative index is assumed to be relative to the end of the array—that is, an index of -1 indicates the last element of the array, -2 is the next to last element in the array, and so on.”
That means, that you may set a[-N]th element if and only |N| <= a.size. That’s why a = [1,2] ; a[-3] = 3 fails (3 > 2).
On the other hand, there is [likely] not documented feature for ruby arrays: a[INBOUNDS_IDX..NONSENSE_IDX]=SMTH will insert SMTH before INBOUNDS_IDX index:
a=[1,2]
a[2..0]='a'
a[2..1]='b'
a[2..-100]='c'
# ⇒ [1, 2, "c", "b", "a"]
a[2..-1]='q'
# ⇒ [1, 2, "q"]
Nonsense here means “less than INBOUNDS_IDX, and not treatable as index in an negative notation” (that’s why a[2..-1] in the example above is treated as a[2..(a.size - 1)].)
Q1:
An empty array has 0 elements, so when you try to set its element 0, with negative index -1, it will give an error.
Because negative index cycles through the array from the end.
So
a = []; a[-1] = 3 makes it impossible to
a) get the element at last position, since its null
b) set its value. since it was never captured.
a[0] = 5 will work because you are telling the compiler to
a) grab the first element,
b) create one if not present, and then assign that to the value you requested.
See official api doc specifically mentioning positive index can grow size, negative index past the beginning of the array raises an error.
Q2:
The above explanation almost answers the second question as well.
Given a = [1,2]
a[-3] = 3 causes the first point of break. You are trying to access the 3rd element from the end, which does not exist. By design it breaks down.
While a[-2..-3] is within the capture range of the defined array.
You ask the interpreter to capture the second element from the last (1st from the front in this case), and try to invoke a range which is asking it to increase the array's size, and populate it with whatever you requested.
Happily, all is still well and as desired. Good to know.
Observation #1
The -1 index is related to the last element, if the array has no size, [], you can't use it until you initialize it with one or more elements.
Observation #2:
Yes, you are right, the negative index never increases size of the array, it only references a concrete existing position in the array.
Don't think the array is circular—the 0 index clued to the N-1 index—so you can't use any negative index thinking that it's valid.

Ruby : Finding lowest free ID in an ID array

I have an array with different IDs going from 1 to 4000. I need to add some elements in a database with an ID that would go in that array. Since the biggest ID possible is 4000 (which is not that much in my case), I'd like to be able to find the lowest unused ID possible I could use for my new element.
I would know how to do that in C++, but since I'm pretty new in Ruby, I'm asking for help. in C++, I would write a loop in which I would check if array[i] == array[i+1] - 1. If not the case, then the new id would be array[i] + 1.
I have just no idea how to write that in Ruby.
Using a range, you can find the first element that is not part of your array:
array = [1,2,3,5,6]
(1..4000).find { |i| !array.include?(i) }
# => 4
array = [1, 2, 3, 5, 6]
(1..4000).to_a.-(array).min
def first_unused_id(ids)
index = ids.each_index.find{|i| ids[i] + 1 != ids[i+1] }
ids[index] + 1
end
Some explanation:
each_index will transform the array into an Enumerator giving the arrays indices.
find will return the first element that returns true from the block passed to it.
how about this one:
(1..4000).find { |i| array[i-1] != i }
similar to Dylan's answer but in this case, it simply checks whether the [n-1]th member of the array is n. If not, that index is "open" and is returned. This solution only requires one check per index, not 4000...
so for
array = [1,2,3,5,6]
this would find that array[4-1] != 4 (because array[3] = 5) and return 4 as the first available id.
(this requires a sorted array of indices but that has been assumed so far)
array = [1, 2, 3, 5, 6]
def lowest_unused(ids)
ids.find { |e| ids.index(e) + 1 != e } - 1
end
p lowest_unused(array) # 4

Resources