Ruby quantify a range - ruby

I have a Ruby array that defines a set of integer thresholds
thresholds = [under_threshold_1, under_threshold_2, ..., under_threshold_n, over_threshold]
I want to map any integer to a value corresponding to the threshold number. Basically
if threshold_a < number < threshold_b
return threshold_a
end
Is there a cool way to do that in Ruby ? I Need to handle the "edge" cases < threshold_1 and > threshold_over. I could only come up with a (ugly but working) set of if statements or looping on the array.
I am actually free to modelise this how I want (I can change the array to something else if more convenient)
I was thinking there is maybe a cool way to splat the thresholds in a case/when clause
case number
when 0..threshold_1 then 0
when threshold_i..threshold_i+1 then i
else n
end
# example
thresholds = [ 4, 8, 10 ,12 ]
quantify(1) = 0
quantify(4) = 1
quantify(11) = 3
quantify(50) = 4

How about this?
thresholds = [ 4, 8, 10, 12 ]
def which_threshold(thresholds, n)
thresholds.find_index {|t| n < t } || thresholds.size
end
p which_threshold(thresholds, 1) # => 0
p which_threshold(thresholds, 4) # => 1
p which_threshold(thresholds, 11) # => 3
p which_threshold(thresholds, 50) # => 4

I think this is what you want:
Thresholds = [4, 8, 10, 12]
def quantify(n)
Thresholds.count { |t| n >= t }
end
This quantification of n you want happens to be the number of thresholds that n is greater than or equal to, and it's easy to compute that using Enumerable#count.

Related

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

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

Ruby Enumeration: Taken first n where block returns true

I want to take the first "n" entries which pass the block
a = 1..100_000_000 # Basically a long array
# This iterates over the whole array -- no good
b = a.select{|x| x.expensive_operation?}.take(n)
I want to short circuit the iteration once i've got n entries where 'expensive' condition is true.
What do you suggest? take_while and keep count of n?
# This is the code i have; which i think can be written better, but how?
a = 1..100_000_000 # Basically a long array
n = 20
i = 0
b = a.take_while do |x|
((i < n) && (x.expensive_operation?)).tap do |r|
i += 1
end
end
Ruby 2.0 implements lazy enumerables, for older versions use the gem enumerable-lazy:
require 'enumerable/lazy'
(1..Float::INFINITY).lazy.select(&:even?).take(5).to_a
#=> [2, 4, 6, 8, 10]
It should work with a simple for loop and a break :
a = 1..100_000_000 # Basically a long array
n = 20
selected = []
for x in a
selected << x if x.expensive_operation?
break if select.length == n
end

How do I value from an array that returns objects at the beginning more often?

Given an array like [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10], I want to get a random value that takes into consideration the position.
I want the likelihood of 1 popping up to be way bigger than 10.
Is something like this possible?
For the sake of simplicity let's assume an array arr = [x, y, z] from which we will be sampling values. We'd like to see following relative frequencies of x, y and z:
frequencies = [5, 2, 1]
Preprocess these frequencies to calculate margins for our subsequent dice roll:
thresholds = frequencies.clone
1.upto(frequencies.count - 1).each { |i| thresholds[i] += thresholds[i - 1] }
Let's sum them up.
max = frequencies.reduce :+
Now choose a random number
roll = 1 + rand max
index = thresholds.find_index { |x| roll <= x }
Return arr[index] as a result. To sum up:
def sample arr, frequencies
# assert arr.count == frequencies.count
thresholds = frequencies.clone
1.upto(frequencies.count - 1).each { |i| thresholds[i] += thresholds[i - 1] }
max = frequencies.reduce :+
roll = 1 + rand(max)
index = thresholds.find_index { |x| roll <= x }
arr[index]
end
Let's see how it works.
data = 80_000.times.map { sample [:x, :y, :z], [5, 2, 1] }
A histogram for data shows that sample works as we've intended.
def coin_toss( arr )
arr.detect{ rand(2) == 0 } || arr.last
end
a = (1..10).to_a
10.times{ print coin_toss( a ), ' ' } #=> 1 1 1 9 1 5 4 1 1 3
This takes the first element of the array, flips a coin, returns the element and stops if the coinflip is 'tails'; the same with the next element otherwise. If it is 'heads' all the way, return the last element.
A simple way to implement this with an logarithmic probabilistic of being selected is to simulate coin flips. Generate a random integer 0 and 1, the index to that array to choose is the number of consecutive 1s you get. With this method, the chance of selecting 2 is 1/2 as likely as 1, 3 is 1/4th as likely, etc. You can vary the probability slightly say by generating random numbers between 0 and 5 and count the number of consecutive rounds above 1, which makes each number in the array 4/5th as likely to appear as the one before.
A better and more general way to solve this problem is to use the alias method. See the answer to this question for more information:
Data structure for loaded dice?

Calculate missing values in an array from adjacent values

Given an array
[50,30,0,0,10,0,30,60,0]
I need to replace the zeroes with calculated values to create a 'curve', so for example, between 10 and 30, the zero could be replaced with 20.
I keep thinking there must be a cool ruby way of doing this, but I cant find one. Can anyone help? The solution needs to take into account multiple adjacent zeroes, and zeroes at the start and end of the range.
Anyone any ideas?
The term you seem to be unaware of is interpolation. The Wikipedia article is a good place to start - exactly what algoithm is best suited to you depends on the exact context of your problem so we can't give you the one true answer here.
a =[50,30,0,0,10,0,30,60,0]
a.each_index{|i| a[i] = a[i-1] - ((a[i-2] - a[i-1])/2).to_i if a[i] == 0 && i > 1 }
puts a.inspect # [50, 30, 20, 15, 10, 8, 30, 60, 75]
I can't work out why the last number might be 80 in your spec however? Plus it doesn't work for the first two items in the array.
If there could not be consecutive zeroes, this unintelligible one-liner would do the trick (list is the given list of numbers):
[0, *list, 0].each_cons(3).map { |p, x, n| x == 0 ? (p + n)/2 : x }
Ruby 1.9 only, I think.
def find_consecutive_values( array, value=nil )
raise "Need a value or block to find" unless value || block_given?
start = last = nil
ranges = []
indices = array.each_with_index do |o,i|
if ((block_given? && yield(o)) || o==value)
start = i unless start
last = i
else
ranges << (start..last) if start && last
start = last = nil
end
end
ranges << (start..last) if start && last
ranges
end
def interpolate_zeros( array, round=false )
result = array.dup
find_consecutive_values( array, 0 ).each do |range|
next unless range.first>0 && range.last<(array.length-1)
before = result[range.first - 1]
after = result[range.last + 1]
diff = after - before
size = (range.last - range.first + 2).to_f
range.each_with_index do |i,idx|
value = before + diff * (idx+1)/size
value = value.round if round
result[i] = value
end
end
result
end
p interpolate_zeros( [0,50,30,0,0,10,0,30,60,0], true )
#=> [0, 50, 30, 23, 17, 10, 20, 30, 60, 0]
Just stumbled across this question. There is a ruby gem "interpolator", which just does what you want and probably tons more:
http://interpolator.rubyforge.org.
Here is a short introduction:
http://fabianosoriani.wordpress.com/2010/02/23/ruby-interpolation-with-gem-interpolator/

Resources