Strange behavior with bsearch_index - ruby

tmp = [-3,3,5]
p "test: #{tmp.bsearch_index{|j| j == -3}}"
In the above code, I get response as nil. If I compare j against 3, or 5, it works. Why does bsearch_index does not consider very first element?

You need to write
tmp.bsearch_index{|n| n >= -3} #=> 0
This uses Array#bsearch_index's find minimum mode, which returns the smallest value in the array that satisfies the expression in the block. For example,
tmp.bsearch { |n| n >= 0 } #=> 3
tmp.bsearch_index { |n| n >= 0 } #=> 1
In this mode, to quote the doc for Array#bsearch, "the block must always return true or false, and there must be an index i (0 <= i <= ary.size) so that the block returns false for any element whose index is less than i, and the block returns true for any element whose index is greater than or equal to i. This method returns the i-th element. If i is equal to ary.size, it returns nil."
If the block were { |n| n == -3 } there would be no index i, 0 <= i <= tmp.size #=> 3 that has the property that tmp[j] == -3 is false for all j < i and true for all j >= 1.
If the block calculation were tmp[j] == 5 the requirement would be satisfied (for index 2) so the correct value would be returned. If the block calculation were tmp[j] == 3 the requirement would not be satisfied (tmp[2] == 3 #=> false); the fact that the correct index is returned is only due to the way the method has been implemented. If
tmp = [-3, 3, 5, 6]
then nil is returned for n == 3 as well as for n == -3:
tmp.bsearch_index { |n| n == 3 } #=> nil
bsearch has a second mode, find any mode. (See the doc for Array#bsearch for details.) In this case we might write one of the following:
tmp.bsearch_index { |n| -3 - n } #=> 0
tmp.bsearch_index { |n| 3 - n } #=> 1
tmp.bsearch_index { |n| 5 - n } #=> 2
tmp.bsearch_index { |n| 4 - n } #=> nil
This mode would be useful here if nil were to be returned when no element in the array evaluates to zero in the block. In other contexts it has a multitude of uses.

Related

How does this recursive code find original index?

I don't understand how it's returning the correct answer
def bsearch(array, target)
return nil if array.length == 0
midpoint = array.length / 2
case target <=> array[midpoint]
when -1
bsearch(array.take(midpoint), target)
when 0
midpoint
when 1
index = bsearch(array.drop(midpoint + 1), target)
(index.nil?) ? nil : (midpoint + 1) + index
end
end
p bsearch([1, 3, 4, 5, 9], 5) # => 3
p bsearch([1, 2, 3, 4, 5, 6], 6) # => 5
Somehow it jumps from line 10 (where it should return midpoint ) to 13 where it somehow has the original array again?!
Here was my attempt but it fails by returning the midpoint of the last recursive version of the array
def bsearch(array, target)
return nil if array.length == 0
aim = array.length / 2
return nil if array.length == 1 && array[0] != target
case target <=> array[aim]
when 0
aim
when -1
bsearch(array[0...aim], target)
when 1
bsearch(array[aim..-1], target)
end
end
The major difference between the code in question and your attempt is this line:
(index.nil?) ? nil : (midpoint + 1) + index
in the last branch. And it actually makes the difference.
The reason is that the algorithm "shrinks" the array on each miss, so the array you're looking in becomes smaller. But you still need the target index in the larger, original array. So, each time you take the "right" subarray and drop the "left" one you need to adjust the index somehow. And this is exactly what that line does.

Using Ruby's array.bsearch_index(), is there an elegant way to look for the closest index in array so that array[i] is less than a number n?

I had to use something like
arr = [10, 20, 50, 80, 110]
(arr.bsearch_index{|a| a >= 50} || arr.length) - 1 # => 1
(arr.bsearch_index{|a| a >= 2000} || arr.length) - 1 # => 4
with the return value -1 meaning there is no such index. What if the numbers could be float, so you cannot look for 49 instead when n is 50. The code right now is a little bit messy. Is there a more elegant way to do it?
(Maybe it is just how bsearch_index() does it: to return nil when not found... so we just have to use bsearch(){ } || arr.length to convert it back to strictly numbers -- so that's just the way it is. bsearch_index has to either return only numbers or it can return nil as a design decision and it chose to return nil. But I am not sure if we just have to use the code above. Maybe the find-any mode of bsearch_index or some kind of way can do it and is more elegant.)
P.S. it might be interesting to use a reverse() operation or negating every element or something, but since those are O(n), it defeats the purpose of using a O(lg n) solution using binary search and we can just do a linear search.
In order to express "less than" directly (i.e. via <), you have to reverse the array:
rindex = arr.reverse.bsearch_index { |a| a < 50 }
#=> 4
To un-reverse the index:
arr.size - rindex - 1
#=> 1
In one line:
arr.reverse.bsearch_index { |a| a < 50 }.yield_self { |i| arr.size - i - 1 if i }
The the modifier-if handles i being nil, e.g. a < 10
Or simply use a descending array in the first place:
arr = [110, 80, 50, 20, 10]
arr.bsearch_index { |a| a < 50 } #=> 3
arr.bsearch_index { |a| a < 2000 } #=> 0
Not completely clear, but for a sorted Array I guess:
arr.bsearch_index{ |a| a >= n }.then { |i| i ? i - 1 : -1}
Or, since nil.to_i #=> 0
arr.bsearch_index{ |a| a >= n }.to_i - 1
# n = 49 #=> 1
# n = 50 #=> 1
# n = 51 #=> 2
# n = 111 #=> -1

Speeding up solution to algorithm

Working on the following algorithm:
Given an array of non-negative integers, you are initially positioned
at the first index of the array.
Each element in the array represents your maximum jump length at that
position.
Determine if you are able to reach the last index.
For example:
A = [2,3,1,1,4], return true.
A = [3,2,1,0,4], return false.
Below is my solution. It tries every single potential step, and then memoizes accordingly. So if the first element is three, the code takes three steps, two steps, and one step, and launches three separate functions from there. I then memoized with a hash. My issue is that the code works perfectly fine, but it's timing out for very large inputs. Memoizing helped, but only a little bit. Am I memoizing correctly or is backtracking the wrong approach here?
def can_jump(nums)
#memo = {}
avail?(nums, 0)
end
def avail?(nums, index)
return true if nums.nil? || nums.empty? || nums.length == 1 || index >= nums.length - 1
current = nums[index]
true_count = 0
until current == 0 #try every jump from the val to 1
#memo[index + current] ||= avail?(nums, index + current)
true_count +=1 if #memo[index + current] == true
current -= 1
end
true_count > 0
end
Here's a 𝑂(𝑛) algorithm:
Initialize π‘šπ‘Žπ‘₯ to 0.
For each number 𝑛𝑖 in 𝑁:
If 𝑖 is greater than π‘šπ‘Žπ‘₯, neither 𝑛𝑖 nor any subsequent number can be reached, so
return false.
If 𝑛𝑖+𝑖 is greater than π‘šπ‘Žπ‘₯, set π‘šπ‘Žπ‘₯ to 𝑛𝑖+𝑖.
If π‘šπ‘Žπ‘₯ is greater than or equal to the last index in 𝑁
return true.
Otherwise return false.
Here's a Ruby implementation:
def can_jump(nums)
max_reach = 0
nums.each_with_index do |num, idx|
return false if idx > max_reach
max_reach = [idx+num, max_reach].max
end
max_reach >= nums.size - 1
end
p can_jump([2,3,1,1,4]) # => true
p can_jump([3,2,1,0,4]) # => false
See it on repl.it: https://repl.it/FvlV/1
Your code is O(n^2), but you can produce the result in O(n) time and O(1) space. The idea is to work backwards through the array keeping the minimum index found so far from which you can reach index n-1.
Something like this:
def can_jump(nums)
min_index = nums.length - 1
for i in (nums.length - 2).downto(0)
if nums[i] + i >= min_index
min_index = i
end
end
min_index == 0
end
print can_jump([2, 3, 1, 1, 4]), "\n"
print can_jump([3, 2, 1, 0, 4]), "\n"

Create a method to find if a number is a power of 2?

I have this code to return true if num is a power of 2.
def is_power_of_two?(num)
result = num.inject(0) {|n1, n2| n2 ** n1}
if result == num
true
else
false
end
end
p is_power_of_two?(16)
I keep getting an error though. How could I fix and simplify this code?
Clearly, n is a non-negative integer.
Code
def po2?(n)
n.to_s(2).count('1') == 1
end
Examples
po2? 0 #=> false
po2? 1 #=> true
po2? 32 #=> true
po2? 33 #=> false
Explanation
Fixnum#to_s provides the string representation of an integer (the receiver) for a given base. The method's argument, which defaults to 10, is the base. For example:
16.to_s #=> "16"
16.to_s(8) #=> "20"
16.to_s(16) #=> "10"
15.to_s(16) #=> "f"
It's base 2 we're interested in. For powers of 2:
1.to_s(2) #=> "1"
2.to_s(2) #=> "10"
4.to_s(2) #=> "100"
8.to_s(2) #=> "1000"
16.to_s(2) #=> "10000"
For a few natural numbers that are are not powers of 2:
3.to_s(2) #=> "11"
5.to_s(2) #=> "101"
11.to_s(2) #=> "1011"
We therefore wish to match binary strings that contain one 1.
Another Way
R = /
\A # match beginning of string ("anchor")
10* # match 1 followed by zero or more zeroes
\z # match end of string ("anchor")
/x # free-spacing regex definition mode
def po2?(n)
(n.to_s(2) =~ R) ? true : false
end
po2?(4) #=> true
po2?(5) #=> false
And one for the road
This uses Fixnum#bit_length and Fixnum#[]:
def po2?(n)
m = n.bit_length-1
n[m] == 1 and m.times.all? { |i| n[i].zero? }
end
po2? 0 #=> false
po2? 1 #=> true
po2? 32 #=> true
po2? 33 #=> false
Try:
def is_power_of_two?(num)
num != 0 && (num & (num - 1)) == 0
end
It is well explained here (for C#, but #GregHewgill's explanation applies here as well)
I would do something like this, using Ruby's Math module.
def power_of_two?(n)
Math.log2(n) % 1 == 0
end
Or, if you wanted to be really cool:
def power_of_two?(n)
(Math.log2(n) % 1).zero?
end
Some IRB output:
2.1.0 :004 > power_of_two?(2)
=> true
2.1.0 :005 > power_of_two?(32768)
=> true
2.1.0 :006 > power_of_two?(65536)
=> true
This method assumes that the input is a positive integer.
Source
Another way to solve this is to go the other way around than most of the answers here - we can use the number 1 to start and find out if the number is the power of two. Like this:
def power_of_two?(num)
product = 1
while product < num
product *= 2
end
product == num
end
We start with 1. Then we multiply the 1 by 2, and keep multiplying by 2 until the product is larger than num (product < num). Once we hit that condition, we stop, exit the loop, and check if it's equal to num (product == num). If it is, the num is the power of 2.
As was pointed out in the comments above, you were getting errors because you're trying to use the inject method on a non-iterable (an int). Here's a solution using the suggested log2
def is_power_of_two?(num)
result = Math.log2(num)
result == Integer(result)
end
Note: will fail with very big numbers close to binaries (like 2 ^ 64 - 1). A foolproof version (but slower) would be:
def is_power_of_two?(num)
while (num % 2 == 0 and num != 0)
num /= 2
end
num == 1
end
Please comment any improvements that any of you may find.
Here is another solution that uses recursion:
def power_of_2?(number)
return true if number == 1
return false if number == 0 || number % 2 != 0
power_of_2?(number / 2)
end
In my opinion, the easiest -- but maybe a little long -- way of doing what you need to do is just writing this recursive method like so:
def power_of_two?(number)
continue = true
if number == 1
return true
end
if number % 2 != 0
return false
else
while continue == true do
if number.to_f / 2.0 == 2.0
continue = false
return true
else
if number % 2 != 0
continue = false
return false
else
number /= 2
continue = true
end
end
end
end
end
One is a power of two (2^0), so it first checks if the number given is 1. If not, it checks if it is odd, because 1 is the only odd number that is a power of two.
If it is odd it returns false and moves on to the else statement. It will check if the number divided by 2 is two, because then it would obviously be a power of 2. It does this as a float, because 5/2 in Ruby would return 2.
If that is false, it then again checks if the number is odd -- unnecessary on the first round, necessary after that. If the number is not odd, it will divide the number by two and then do the loop another time.
This will continue until the program resolves itself by getting 2 or any odd number, and returns true or false, respectively.
I ran into this one in a bootcamp application prep. I'm not a math person and don't understand a few of these methods, so I wanted to submit a common sense approach for people like me. this requires little knowledge of math, except to know a number to the second power will be the result of some number multiplied by itself.
def is_power_of_two?(num)
num.times {|n| return true if (n+1) * (n+1) == num}
false
end
this method counts up to the num variable starting at 1 and returns true if (any of those numbers in the sequence multiplied by itself) is equal to num & if num is not 0 (more on that below).
example:
num = 9
1 * 1 == 9 #=> false
2 * 2 == 9 #=> false
3 * 3 == 9 #=> true
true is returned and method is finished running.
the #times method requires an integer > 0, so this edge case is "handled" by virtue of the fact that #times does nothing with "0" as the variable and returns false when outside of the #times iteration.
def power_of_two?(num)
num.to_s(2).scan(/1/).length == 1
end

Count iterations performed on enumerable object

I need to select from an array the elements that satisfy some condition, and count the number of iterations it took. For example:
array = [1,2,3,4,5...1,000,000]
count = 0
array.keep_if { |x| x % 2 == 0 }
I want to increase the counter every time the condition x % 2 == 0 is satisfied. Is there a way to tell how many iterations took place?
I think this should work as expected.
array.keep_if { |x|
keep = (x % 2 == 0 ? true : false)
count = count + 1 if keep
keep
}
Just out of curiosity, whether you won’t call Array#size on result:
array.reduce([0, []]) { |memo, el|
next memo unless el % 2 == 0
memo[0] += 1
memo.last << el
memo
}
#β‡’ [
# [0] 500,
# [1] [ ... ] # the filtered result
#]
I would chose a simpler solution. The count you seek will be equivalent with the length of the resulting array. So you can use a classic select method and then count the results.
array = (1..1000000).to_a
[res = array.select {|x| x % 2 == 0}, count = res.length]
puts the reduced array to res, and the number of true conditions on count

Resources