manual binary search wrap up - ruby

Would anyone be so kind to explain to me how I finish my recursive binary search problem? The recursive aspect is confusing me. I would love for an explanation on what thats doing if possible!!! I think I need to increment the 'half' value I have within the if or elsif but I don't know what it would look like. Please suggest ways to add to the code I currently have rather than refactor to something simpler... at least at first! Thanks!
def binary_search(letter, array)
half = (array.length - 1)/2
if letter == array[half]
return half
end
if letter > array[half] && letter <= array[-1]
array = array[half...array.length]
binary_search(letter, array)
elsif letter < array[half] && letter >= array[0]
array = array[0...half]
binary_search(letter, array)
else
nil
end
end
arr = [:A, :B, :C, :D, :E, :F, :G]
p binary_search(:C, arr)

half was part of the problem. With a length of 2, half would be 0, and you would "split" your array in a full array and an empty array : recursion would never end.
You also need to keep an index, and add half to it when you consider the 2nd Array :
def binary_search(letter, array, i=0)
puts "Been here for #{array} with #{i}"
half = array.length / 2
if letter == array[half]
return i + half
end
if letter > array[half] && letter <= array[-1]
binary_search(letter, array.drop(half), i + half)
elsif letter < array[half] && letter >= array[0]
binary_search(letter, array.take(half), i)
else
nil
end
end
arr = [:A, :B, :C, :D, :E, :F, :G]
p binary_search(:C, arr)
p binary_search(:G, arr)
It outputs
Been here for [:A, :B, :C, :D, :E, :F, :G] with 0
Been here for [:A, :B, :C] with 0
Been here for [:B, :C] with 1
2
Been here for [:A, :B, :C, :D, :E, :F, :G] with 0
Been here for [:D, :E, :F, :G] with 3
Been here for [:F, :G] with 5
6

Related

Change the index of place, according to the created method (RUBY challenge)

My question is:
array = [0, 1]
and the mandatory instruction of the challenge is to use "instr" to change the index positions:
instr = "0 <> 1"
From that, I have to swap the index places, according to "instr", as a method.
The question is, how?
You need to extract the indexes from the instr first and assign them to a variable (swap in my example). Then you can swap the values at the indexes in a one-liner.
array = [:a, :b, :c, :d]
instr = "0 <> 2"
i, j = instr.split("<>").map(&:to_i) #=> [0, 2]
array[i], array[j] = array[j], array[i]
puts array
#=> [:c, :b, :a, :d]

Ruby, equally distribute elements and interleave/merge multiple arrays

I have multiple arrays with unknown element count like
a = []
a << [:a, :c, :e]
a << [:b, :f, :g, :h, :i, :j]
a << [:d]
result should be something like ~ (I don't really care details due rounding etc)
r = [:b, :a, :f, :g, :d, :c, :h, :i, :e, :j]
This is how I think it could be done
First we need to expand/distribute equally elements in each array to same length, so we get something like
a << [nil, :a, nil, :c, nil, :e]
a << [:b, :f, :g, :h, :i, :j]
a << [nil, nil, :d, nil, nil]
Next we interleave them as typically would do
r = a.shift
a.each { |e| r = r.zip(e) }
r = r.flatten.compact
My current problem is how to equally (as much as it's possible) distribute those elements across array? There could be one array with 4 elements and other with 5, but probably biggest should go first.
Of course would be nice to see if there's any other way to achieve this :)
I would use a sort to do this, based on element index postion, divided by size of array, plus some offset based on array id, to keep things consistent (if you don't need consistency, you could use a small random offset instead).
a = [:a,:b]
b = [:c]
c = [:d,:e,:f]
d = [:g:,:h,:i,:j]
def sort_pos array, id
(1..array.size).map { |i| (i - 0.5 + id/1000.0)/(array.size + 1e-6) }
end
# Combine all the arrays with their sort index, assigning ids to each array for consistency.
# Depending on how you receive these arrays, this structure can be built up programatically,
# as long as you add an array plus its sort index numbers at the same time
combined = (a + b + c + d).zip( sort_pos(a, 1) + sort_pos(b, 2) + sort_pos(c, 3) + sort_pos(d, 4) )
# Extract the values from the original arrays in their new order
combined.sort_by { |zipped| zipped[1] }.map { |zipped| zipped[0] }
=> [:g, :d, :a, :h, :e, :i, :b, :f, :j, :c]
There might be a cleaner way of doing this in Ruby . . . but I think the end result is what you are after - an "even" mix of multiple arrays.
If you only care about even-ness of mix from a statistical perspective (i.e. over time it is "fair"), you could just do this:
(a+b+c+d).shuffle
=> [:g, :b, :i, :c, :a, :h, :e, :j, :f, :d]

Rearranging elements

What is the easiest way to rearrange the elements of an array by referencing their indice? Something like this:
[:a, :b, :c, :d].rearrange(3, 1, 0, 2) # => [:d, :b, :a, :c]
[:a, :b, :c, :d].rearrange!(3, 1, 0, 2) # => [:d, :b, :a, :c]
Well, Ruby already has this method, only that it's called values_at:
>> [:a, :b, :c, :d].values_at(3, 1, 0, 2)
=> [:d, :b, :a, :c]
I'd turn it around a bit. A single permutation is just a function which maps an Array to another Array so this seems natural:
a = [:a, :b, :c, :d]
permuted = [3, 1, 0, 2].map { |i| a[i] }
That's easy to monkey patch into Array if necessary:
class Array
def permute(*p)
p.map { |i| self[i] }
end
end
Error checking, permute!, and further niceties are left as an exercise.

finding out which object has n instances in ruby

I have an array: x = [a, b, c, d, e, f, g, h] which can have objects from 1 to 9
Firstly, I have to count IF any of these objects is present 3 times. I don't want to write
if (x.count(1) == 3) or (x.count(2) == 3) ...etc...
is there a way to shorten this, like below?
x.count { |obj| obj } == 3
Secondly, if I know that an object has been found with 3 instances, how can I find out which one was it? (1 or 2 or 3.....)
x = [:a, :b, :b, :b, :c, :c, :c]
counted = Hash[
x.group_by do |e|
x.count(e)
end.map do |count, items|
[count, items.uniq]
end
]
p counted[3] #=> [:b, :c]
How does this work? Let's follow the steps. First, let's group the items by count:
grouped_by_count = x.group_by do |e|
x.count(e)
end
This produces a hash with the keys being the counts, and the values being the list of non-unique items having that count:
p grouped_by_count
#=> {1=>[:a], 3=>[:b, :b, :b, :c, :c, :c]}
We'd really rather have unique items, though, so let's do that transform:
grouped_by_count_unique = grouped_by_count.map do |count, items|
[count, items.uniq]
end
p grouped_by_count_unique
#=> [[1, [:a]], [3, [:b, :c]]]
That gives us an array of arrays, and not a hash. Fortunately, it's easy to turn an array of arrays into a hash:
counted = Hash[grouped_by_count_unique]
p counted
# => {1=>[:a], 3=>[:b, :c]}
Now just put the pieces together eliminating the temporaries and you get the answer at the top.

What are the uses of ruby's Hash.replace or Array.replace?

I always see replace in the Array and Hash documentation and I always think that it's odd.
I'm sure I've done something like this many times:
a = [:a, :b, :c, :d]
...
if some_condition
a = [:e, :f]
end
But I never thought to use this instead:
a = [:a, :b, :c, :d]
...
if some_condition
a.replace [:e, :f]
end
Which I assume is the intended use. Does this really save memory, or have some other benefit, or is it just a style thing?
a = [:e, :f] and a.replace [:e, :f],
the two statements generated instructions as follows:
1.
a = [:a, :b, :c, :d]
a = [:e, :f]
instructions:
ruby --dump=insns test.rb
== disasm: <RubyVM::InstructionSequence:<main>#test.rb>=================
local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1] s1)
[ 2] a
0000 trace 1 ( 1)
0002 duparray [:a, :b, :c, :d]
0004 setdynamic a, 0
0007 trace 1 ( 2)
0009 duparray [:e, :f]
0011 dup
0012 setdynamic a, 0
0015 leave
2.
a = [:a, :b, :c, :d]
a.replace([:e, :f])
instructions:
ruby --dump=insns test.rb
== disasm: <RubyVM::InstructionSequence:<main>#test.rb>=================
local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1] s1)
[ 2] a
0000 trace 1 ( 1)
0002 duparray [:a, :b, :c, :d]
0004 setdynamic a, 0
0007 trace 1 ( 2)
0009 getdynamic a, 0
0012 duparray [:e, :f]
0014 send :replace, 1, nil, 0, <ic:0>
0020 leave
The replace method is not faster than assignment operator, but replace can modify receiver array in-place, and, replace method really save memory, this can be seen from rb_ary_replace's source.
VALUE
rb_ary_replace(VALUE copy, VALUE orig)
{
rb_ary_modify_check(copy);
orig = to_ary(orig);
if (copy == orig) return copy;
if (RARRAY_LEN(orig) <= RARRAY_EMBED_LEN_MAX)
{
VALUE *ptr;
VALUE shared = 0;
if (ARY_OWNS_HEAP_P(copy))
{
xfree(RARRAY_PTR(copy));
}
else if (ARY_SHARED_P(copy))
{
shared = ARY_SHARED(copy);
FL_UNSET_SHARED(copy);
}
FL_SET_EMBED(copy);
ptr = RARRAY_PTR(orig);
MEMCPY(RARRAY_PTR(copy), ptr, VALUE, RARRAY_LEN(orig));
if (shared)
{
rb_ary_decrement_share(shared);
}
ARY_SET_LEN(copy, RARRAY_LEN(orig));
}
else
{
VALUE shared = ary_make_shared(orig);
if (ARY_OWNS_HEAP_P(copy))
{
xfree(RARRAY_PTR(copy));
}
else
{
rb_ary_unshare_safe(copy);
}
FL_UNSET_EMBED(copy);
ARY_SET_PTR(copy, RARRAY_PTR(orig));
ARY_SET_LEN(copy, RARRAY_LEN(orig));
rb_ary_set_shared(copy, shared);
}
return copy; }
I think the intended use is to modify an array in-place that has been passed to a method. For example:
def m(a)
a.replace(%w[a b])
end
a = %w[x y z]
m(a)
# a is now ['a', 'b']
Without replace, you'd have to do something like this:
def m(a)
a.clear
a << 'a' # or use .push of course
a << 'b'
end
Using replace lets you do it all at once should bypass the auto-shrinking and auto-growing (which probably involves copying some memory) behavior that would be a side effect of replacing the array's content (not the array itself!) element by element. The performance benefit (if any) is probably just an extra, the primary intent is probably to get pointer-to-pointer behavior without having to introduce pointers or wrap the array in an extra object.
a = [:a, :b, :c, :d]
b = [:x, :y, :z]
a.replace(b)
a.object_id == b.object_id
=> false
a = [:a, :b, :c, :d]
b = [:x, :y, :z]
a = b
a.object_id == b.object_id
=> true
Also
a = [:a, :b, :c, :d]
c = a
b = [:x, :y, :z]
a.replace(b)
p c # => [:x, :y, :z]
vs
a = [:a, :b, :c, :d]
c = a
b = [:x, :y, :z]
a = b
p c # => [:a, :b, :c, :d]
This does not answer your question exactly.

Resources