Sorting a subsequence of an array without an additional array - ruby

I'm new to programming. I start to learn Ruby freshly and I can't understand how to implement an algorithm for permuting negative(or positive) elements of an array without using an additional array.
For example:
Like this: [-10, 3, -1, 0, -9 ] -> [-10, 3, -9, 0, -1 ] (sort only negatives)
Either that: [-5, 5, -8, 2, 1] -> [-5, 1, -8, 2, 5] (sort only positives)

def sort_positives(arr)
(arr.size-1).times do |i|
next if arr[i] <= 0
smallest = i
(i+1..arr.size-1).each do |j|
smallest = j if arr[j] > 0 && arr[j] < arr[smallest]
end
if smallest > i
tmp = arr[i]
arr[i] = arr[smallest]
arr[smallest] = tmp
end
end
arr
end
arr = [-2, 5, 3, -4, 6, 1, -3]
sort_positives arr
#=> [-2, 1, 3, -4, 5, 6, -3]
arr
#=> [-2, 1, 3, -4, 5, 6, -3]
sort_negatives is a straightforward modification to sort_positives.

def swap(arr, i, j)
arr[i] = arr[i] ^ arr[j]
arr[j] = arr[j] ^ arr[i]
arr[i] = arr[j] ^ arr[i]
end
def sort_negative(arr)
size = arr.size
i = 0
while i < size - 1
next if arr[i] > 0 # first index of negative number
next_neg = 0
min_neg = i
(i + 1).upto(size - 1) do |j|
if arr[i] < 0 && arr[j] < 0
next_neg = j if next_neg == 0
min_neg = j if arr[min_neg] > arr[j]
end
end
swap(arr, i, min_neg) if min_neg > i
if next_neg > 0
i = next_neg # jump to next negative number
else
break
end
end
end
arr = [-10, 0, 2, 6, 10, -1, 7, 20, 1, 0, -9, 13, 6, 88, -11, -156]
sort_negative(arr)
puts arr.inspect # [-156, 0, 2, 6, 10, -11, 7, 20, 1, 0, -10, 13, 6, 88, -9, -1]

Related

Linearly reading a multi-dimensional array obeying dimensional sub-sectioning

I have an API for reading multi-dimensional arrays, requiring to pass a vector of ranges to read sub-rectangles (or hypercubes) from the backing array. I want to read this array "linearly", all elements in some given order with arbitrary chunk sizes. Thus, the task is with an off and a len, to translate the elements covered by this range into the smallest possible set of hyper-cubes, i.e. the smallest number of read commands issued in the API.
For example, we can calculate index vectors for the set of dimensions giving a linear index:
def calcIndices(off: Int, shape: Vector[Int]): Vector[Int] = {
val modsDivs = shape zip shape.scanRight(1)(_ * _).tail
modsDivs.map { case (mod, div) =>
(off / div) % mod
}
}
Let's say the shape is this, representing an array with rank 4 and 120 elements in total:
val sz = Vector(2, 3, 4, 5)
val num = sz.product // 120
A utility to print these index vectors for a range of linear offsets:
def printIndices(off: Int, len: Int): Unit =
(off until (off + len)).map(calcIndices(_, sz))
.map(_.mkString("[", ", ", "]")).foreach(println)
We can generate all those vectors:
printIndices(0, num)
[0, 0, 0, 0]
[0, 0, 0, 1]
[0, 0, 0, 2]
[0, 0, 0, 3]
[0, 0, 0, 4]
[0, 0, 1, 0]
[0, 0, 1, 1]
[0, 0, 1, 2]
[0, 0, 1, 3]
[0, 0, 1, 4]
[0, 0, 2, 0]
[0, 0, 2, 1]
[0, 0, 2, 2]
[0, 0, 2, 3]
[0, 0, 2, 4]
[0, 0, 3, 0]
[0, 0, 3, 1]
[0, 0, 3, 2]
[0, 0, 3, 3]
[0, 0, 3, 4]
[0, 1, 0, 0]
...
[1, 2, 1, 4]
[1, 2, 2, 0]
[1, 2, 2, 1]
[1, 2, 2, 2]
[1, 2, 2, 3]
[1, 2, 2, 4]
[1, 2, 3, 0]
[1, 2, 3, 1]
[1, 2, 3, 2]
[1, 2, 3, 3]
[1, 2, 3, 4]
Let's look at an example chunk that should be read,
the first six elements:
val off1 = 0
val len1 = 6
printIndices(off1, len1)
I will already partition the output by hand into hypercubes:
// first hypercube or read
[0, 0, 0, 0]
[0, 0, 0, 1]
[0, 0, 0, 2]
[0, 0, 0, 3]
[0, 0, 0, 4]
// second hypercube or read
[0, 0, 1, 0]
So the task is to define a method
def partition(shape: Vector[Int], off: Int, len: Int): List[Vector[Range]]
which outputs the correct list and uses the smallest possible list size.
So for off1 and len1, we have the expected result:
val res1 = List(
Vector(0 to 0, 0 to 0, 0 to 0, 0 to 4),
Vector(0 to 0, 0 to 0, 1 to 1, 0 to 0)
)
assert(res1.map(_.map(_.size).product).sum == len1)
A second example, elements at indices 6 until 22, with manual partitioning giving three hypercubes or read commands:
val off2 = 6
val len2 = 16
printIndices(off2, len2)
// first hypercube or read
[0, 0, 1, 1]
[0, 0, 1, 2]
[0, 0, 1, 3]
[0, 0, 1, 4]
// second hypercube or read
[0, 0, 2, 0]
[0, 0, 2, 1]
[0, 0, 2, 2]
[0, 0, 2, 3]
[0, 0, 2, 4]
[0, 0, 3, 0]
[0, 0, 3, 1]
[0, 0, 3, 2]
[0, 0, 3, 3]
[0, 0, 3, 4]
// third hypercube or read
[0, 1, 0, 0]
[0, 1, 0, 1]
expected result:
val res2 = List(
Vector(0 to 0, 0 to 0, 1 to 1, 1 to 4),
Vector(0 to 0, 0 to 0, 2 to 3, 0 to 4),
Vector(0 to 0, 1 to 1, 0 to 0, 0 to 1)
)
assert(res2.map(_.map(_.size).product).sum == len2)
Note that for val off3 = 6; val len3 = 21, we would need four readings.
The idea of the following algorithm is as follows:
a point-of-interest (poi) is the left-most position
at which two index representations differ
(for example for [0, 0, 0, 1] and [0, 1, 0, 0] the poi is 1)
we recursively sub-divide the original (start, stop) linear index range
we use motions in two directions, first by keeping the start constant
and decreasing the stop through a special "ceil" operation on the start,
later by keeping the stop constant and increasing the start through
a special "floor" operation on the stop
for each sub range, we calculate the poi of the boundaries, and
we calculate "trunc" which is ceil or floor operation described above
if this trunc value is identical to its input, we add the entire region
and return
otherwise we recurse
the special "ceil" operation takes the previous start value and
increases the element at the poi index and zeroes the subsequent elements;
e.g. for [0, 0, 1, 1] and poi = 2, the ceil would be [0, 0, 2, 0]
the special "floor" operation takes the previous stop value and
zeroes the elements after the poi index;
e.g. for [0, 0, 1, 1], and poi = 2, the floor would be [0, 0, 1, 0]
Here is my implementation. First, a few utility functions:
def calcIndices(off: Int, shape: Vector[Int]): Vector[Int] = {
val modsDivs = (shape, shape.scanRight(1)(_ * _).tail, shape.indices).zipped
modsDivs.map { case (mod, div, idx) =>
val x = off / div
if (idx == 0) x else x % mod
}
}
def calcPOI(a: Vector[Int], b: Vector[Int], min: Int): Int = {
val res = (a.drop(min) zip b.drop(min)).indexWhere { case (ai,bi) => ai != bi }
if (res < 0) a.size else res + min
}
def zipToRange(a: Vector[Int], b: Vector[Int]): Vector[Range] =
(a, b).zipped.map { (ai, bi) =>
require (ai <= bi)
ai to bi
}
def calcOff(a: Vector[Int], shape: Vector[Int]): Int = {
val divs = shape.scanRight(1)(_ * _).tail
(a, divs).zipped.map(_ * _).sum
}
def indexTrunc(a: Vector[Int], poi: Int, inc: Boolean): Vector[Int] =
a.zipWithIndex.map { case (ai, i) =>
if (i < poi) ai
else if (i > poi) 0
else if (inc) ai + 1
else ai
}
Then the actual algorithm:
def partition(shape: Vector[Int], off: Int, len: Int): List[Vector[Range]] = {
val rankM = shape.size - 1
def loop(start: Int, stop: Int, poiMin: Int, dir: Boolean,
res0: List[Vector[Range]]): List[Vector[Range]] =
if (start == stop) res0 else {
val last = stop - 1
val s0 = calcIndices(start, shape)
val s1 = calcIndices(stop , shape)
val s1m = calcIndices(last , shape)
val poi = calcPOI(s0, s1m, poiMin)
val ti = if (dir) s0 else s1
val to = if (dir) s1 else s0
val st = if (poi >= rankM) to else indexTrunc(ti, poi, inc = dir)
val trunc = calcOff(st, shape)
val split = trunc != (if (dir) stop else start)
if (split) {
if (dir) {
val res1 = loop(start, trunc, poiMin = poi+1, dir = true , res0 = res0)
loop (trunc, stop , poiMin = 0 , dir = false, res0 = res1)
} else {
val s1tm = calcIndices(trunc - 1, shape)
val res1 = zipToRange(s0, s1tm) :: res0
loop (trunc, stop , poiMin = poi+1, dir = false, res0 = res1)
}
} else {
zipToRange(s0, s1m) :: res0
}
}
loop(off, off + len, poiMin = 0, dir = true, res0 = Nil).reverse
}
Examples:
val sz = Vector(2, 3, 4, 5)
partition(sz, 0, 6)
// result:
List(
Vector(0 to 0, 0 to 0, 0 to 0, 0 to 4), // first hypercube
Vector(0 to 0, 0 to 0, 1 to 1, 0 to 0) // second hypercube
)
partition(sz, 6, 21)
// result:
List(
Vector(0 to 0, 0 to 0, 1 to 1, 1 to 4), // first read
Vector(0 to 0, 0 to 0, 2 to 3, 0 to 4), // second read
Vector(0 to 0, 1 to 1, 0 to 0, 0 to 4), // third read
Vector(0 to 0, 1 to 1, 1 to 1, 0 to 1) // fourth read
)
The maximum number of reads, if I'm not mistaken, would be 2 * rank.

Sum of two sums in Ruby with two loops

If a pair of numbers in the array sums to zero, I want the positions of those two numbers. If no pair of numbers sums to zero, i should return nil.
The iteration not happening in my outer loop:
def two_sum(nums)
# puts(nums)
l = nums.length
i = 0
j = 1
while(i < l)
while(j < l)
puts(nums[i] + nums[j])
puts(nums[i])
puts(nums[j])
if(nums[i] + nums[j] == 0)
return ("[" + i.to_s + "," + j.to_s + "]")
puts("[" + i.to_s + "," + j.to_s + "]")
else
j = j + 1
end
end
i = i + 1
end
end
It's much simpler to use ranges and each; this makes the code much clearer and more concise:
#!/usr/bin/env ruby
def two_sum(nums)
(0...nums.length).each do |i|
((i+1)...nums.length).each do |j|
return [i, j] if nums[i] + nums[j] == 0
end
end
nil
end
p two_sum([1, 2, 3, -1, 4]) # [0, 3]
p two_sum([1, 2, 3]) # nil
p two_sum([]) # nil
The problem is that you set the value of j = 1 only at the beginning, but you need it reset for each outer iteration. Just move the j = 1 after the
while(i < l)
or even better: j = i + 1
As ✅ has answered you question, I would like to suggest an alternative:
def pair_sums_to_zero(arr)
h = arr.each_with_index.group_by { |n,_| n.abs }
return h[0].first(2).map(&:last) if h.key?(0) and h[0].size > 1
a = h.map { |k,v| v.uniq(&:first) }.find { |b| b.size == 2 }
a ? a.map(&:last) : nil
end
arr = [3,2,-4,-2,3,2]
pair_sums_to_zero arr
#=> [1,3]
The steps:
h = arr.each_with_index.group_by { |n,_| n.abs }
#=> {3=>[[3, 0], [3, 4]], 2=>[[2, 1], [-2, 3], [2, 5]], 4=>[[-4, 2]]}
h.key?(0) and h[0].size > 1
#=> false
c = h.map { |k,v| v.uniq(&:first) }
#=> [[[3, 0]], [[2, 1], [-2, 3]], [[-4, 2]]]
a = c.find { |b| b.size == 2 }
a ? a.map(&:last) : nil
#=> [[2, 1], [-2, 3]]
a ? a.map(&:last) : nil
#=> [1, 3]
Another example:
arr = [3,0,2,-4,-6,0,3,0,2]
pair_sums_to_zero arr
h = arr.each_with_index.group_by { |n,_| n.abs }
#=> {3=>[[3, 0], [3, 6]], 0=>[[0, 1], [0, 5], [0, 7]], 2=>[[2, 2], [2, 8]],
# 4=>[[-4, 3]], 6=>[[-6, 4]]}
h.key?(0) and h[0].size > 1
#=> true
h[0].first(2).map(&:last)
#=> [1, 5] (returned)

What's wrong with my selection sort algorithm?

The answer might be obvious to the trained eye, but I've been hitting the books for a few hours now, my eyes are straining, and I can't seem to see the bug.
Below are two implementations of selection sort I wrote, and neither is sorting the input correctly. You can play with this code on an online interpreter.
def selection_sort_enum(array)
n = array.length - 1
0.upto(n - 1) do |i|
smallest = i
(i + 1).upto(n) do |j|
smallest = j if array[j] < array[i]
end
array[i], array[smallest] = array[smallest], array[i] if i != smallest
end
end
def selection_sort_loop(array)
n = array.length - 1
i = 0
while i <= n - 1
smallest = i
j = i + 1
while j <= n
smallest = j if array[j] < array[i]
j += 1
end
array[i], array[smallest] = array[smallest], array[i] if i != smallest
i += 1
end
end
Here's the test of the first implementation, selection_sort_enum:
puts "Using enum:"
a1 = [*1..10].shuffle
puts "Before sort: #{a1.inspect}"
selection_sort_enum(a1)
puts "After sort: #{a1.inspect}"
Here's the test of the second implementation, selection_sort_loop:
puts "Using while:"
a2 = [*1..10].shuffle
puts "Before sort: #{a2.inspect}"
selection_sort_enum(a2)
puts "After sort: #{a2.inspect}"
Here's the output of the first implementation, selection_sort_enum:
Using enum:
Before sort: [7, 5, 2, 10, 6, 1, 3, 4, 8, 9]
After sort: [4, 3, 1, 9, 5, 2, 6, 7, 8, 10]
Here's the output of the second implementation, selection_sort_loop:
Using while:
Before sort: [1, 10, 5, 3, 7, 4, 8, 9, 6, 2]
After sort: [1, 2, 4, 3, 6, 5, 7, 8, 9, 10]
In both the code snippets you are comparing with index i instead of index smallest.
This should work :
def selection_sort_enum(array)
n = array.length - 1
0.upto(n - 1) do |i|
smallest = i
(i + 1).upto(n) do |j|
smallest = j if array[j] < array[smallest]
end
array[i], array[smallest] = array[smallest], array[i] if i != smallest
end
end
def selection_sort_loop(array)
n = array.length - 1
i = 0
while i <= n - 1
smallest = i
j = i + 1
while j <= n
smallest = j if array[j] < array[smallest]
j += 1
end
array[i], array[smallest] = array[smallest], array[i] if i != smallest
i += 1
end
end
Output :
Using enum:
Before sort: [5, 6, 7, 9, 2, 4, 8, 1, 10, 3]
After sort: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Using while:
Before sort: [6, 5, 9, 2, 1, 3, 10, 4, 7, 8]
After sort: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Link to solution : http://ideone.com/pKLriY
def selection_sort_enum(array)
n = array.length - 1
0.upto(n) do |i| # n instead of (n - 1)
smallest_index = i
(i + 1).upto(n) do |j|
smallest_index = j if array[j] < array[i]
end
puts "#{array}", smallest_index
array[i], array[smallest_index] = array[smallest_index], array[i] if i != smallest_index
end
end
You might be interested in this:
def selection_sort_enum(array)
n = array.length - 1
0.upto(n - 1) do |i|
smallest = i
(i + 1).upto(n) do |j|
smallest = j if array[j] < array[i]
end
array[i], array[smallest] = array[smallest], array[i] if i != smallest
end
array # <-- added to return the modified array
end
def selection_sort_loop(array)
n = array.length - 1
i = 0
while i <= n - 1
smallest = i
j = i + 1
while j <= n
smallest = j if array[j] < array[i]
j += 1
end
array[i], array[smallest] = array[smallest], array[i] if i != smallest
i += 1
end
array # <-- added to return the modified array
end
require 'fruity'
ARY = (1 .. 100).to_a.shuffle
compare do
_enum { selection_sort_enum(ARY.dup) }
_loop { selection_sort_loop(ARY.dup) }
end
Which results in:
# >> Running each test once. Test will take about 1 second.
# >> _enum is faster than _loop by 3x ± 1.0

Nested if else inside .each iteration

I'm wondering if this makes sense or if the syntax is wrong and basically if this is acceptable. I wanted to nest an if/else condition within my iteration of the array.
def change_numbers(first_array, second_array)
second_array.each do |index|
if first_array[index] == 0
first_array[index] = 1
else
first_array[index] = 0
end
end
end
The array is a simple (binary) array and will only consist of 0s and 1s and I want to use the second array's elements as the indices of the first array that I am going to change.
Example:
first_array = [0, 0, 0, 0, 1, 1, 1, 1, 1]
second_array = [3, 5, 7]
Result:
first_array = [0, 0, 0, 1, 1, 0, 1, 0, 1]
If you don't want to use an if/else you can do:
second_array.each do |index|
first_array[index] = (first_array[index] + 1) % 2
end
def change_numbers(first_array, second_array)
second_array.each { |index| first_array[index] = 1 - first_array[index] }
end
A bit-wise XOR:
ar = [0, 0, 0, 0, 1, 1, 1, 1, 1]
indices = [3, 5, 7]
indices.each{|i| ar[i] ^= 1 }
You can try this -
def change_numbers(first_array, second_array)
second_array.each do |index|
first_array[index] = ((first_array[index] == 0) ? 1 : 0)
end
end

How to find the insertion point in an array using binary search?

The basic idea of binary search in an array is simple, but it might return an "approximate" index if the search fails to find the exact item. (we might sometimes get back an index for which the value is larger or smaller than the searched value).
For looking for the exact insertion point, it seems that after we got the approximate location, we might need to "scan" to left or right for the exact insertion location, so that, say, in Ruby, we can do arr.insert(exact_index, value)
I have the following solution, but the handling for the part when begin_index >= end_index is a bit messy. I wonder if a more elegant solution can be used?
(this solution doesn't care to scan for multiple matches if an exact match is found, so the index returned for an exact match may point to any index that correspond to the value... but I think if they are all integers, we can always search for a - 1 after we know an exact match is found, to find the left boundary, or search for a + 1 for the right boundary.)
My solution:
DEBUGGING = true
def binary_search_helper(arr, a, begin_index, end_index)
middle_index = (begin_index + end_index) / 2
puts "a = #{a}, arr[middle_index] = #{arr[middle_index]}, " +
"begin_index = #{begin_index}, end_index = #{end_index}, " +
"middle_index = #{middle_index}" if DEBUGGING
if arr[middle_index] == a
return middle_index
elsif begin_index >= end_index
index = [begin_index, end_index].min
return index if a < arr[index] && index >= 0 #careful because -1 means end of array
index = [begin_index, end_index].max
return index if a < arr[index] && index >= 0
return index + 1
elsif a > arr[middle_index]
return binary_search_helper(arr, a, middle_index + 1, end_index)
else
return binary_search_helper(arr, a, begin_index, middle_index - 1)
end
end
# for [1,3,5,7,9], searching for 6 will return index for 7 for insertion
# if exact match is found, then return that index
def binary_search(arr, a)
puts "\nSearching for #{a} in #{arr}" if DEBUGGING
return 0 if arr.empty?
result = binary_search_helper(arr, a, 0, arr.length - 1)
puts "the result is #{result}, the index for value #{arr[result].inspect}" if DEBUGGING
return result
end
arr = [1,3,5,7,9]
b = 6
arr.insert(binary_search(arr, b), b)
p arr
arr = [1,3,5,7,9,11]
b = 6
arr.insert(binary_search(arr, b), b)
p arr
arr = [1,3,5,7,9]
b = 60
arr.insert(binary_search(arr, b), b)
p arr
arr = [1,3,5,7,9,11]
b = 60
arr.insert(binary_search(arr, b), b)
p arr
arr = [1,3,5,7,9]
b = -60
arr.insert(binary_search(arr, b), b)
p arr
arr = [1,3,5,7,9,11]
b = -60
arr.insert(binary_search(arr, b), b)
p arr
arr = [1]
b = -60
arr.insert(binary_search(arr, b), b)
p arr
arr = [1]
b = 60
arr.insert(binary_search(arr, b), b)
p arr
arr = []
b = 60
arr.insert(binary_search(arr, b), b)
p arr
and result:
Searching for 6 in [1, 3, 5, 7, 9]
a = 6, arr[middle_index] = 5, begin_index = 0, end_index = 4, middle_index = 2
a = 6, arr[middle_index] = 7, begin_index = 3, end_index = 4, middle_index = 3
a = 6, arr[middle_index] = 5, begin_index = 3, end_index = 2, middle_index = 2
the result is 3, the index for value 7
[1, 3, 5, 6, 7, 9]
Searching for 6 in [1, 3, 5, 7, 9, 11]
a = 6, arr[middle_index] = 5, begin_index = 0, end_index = 5, middle_index = 2
a = 6, arr[middle_index] = 9, begin_index = 3, end_index = 5, middle_index = 4
a = 6, arr[middle_index] = 7, begin_index = 3, end_index = 3, middle_index = 3
the result is 3, the index for value 7
[1, 3, 5, 6, 7, 9, 11]
Searching for 60 in [1, 3, 5, 7, 9]
a = 60, arr[middle_index] = 5, begin_index = 0, end_index = 4, middle_index = 2
a = 60, arr[middle_index] = 7, begin_index = 3, end_index = 4, middle_index = 3
a = 60, arr[middle_index] = 9, begin_index = 4, end_index = 4, middle_index = 4
the result is 5, the index for value nil
[1, 3, 5, 7, 9, 60]
Searching for 60 in [1, 3, 5, 7, 9, 11]
a = 60, arr[middle_index] = 5, begin_index = 0, end_index = 5, middle_index = 2
a = 60, arr[middle_index] = 9, begin_index = 3, end_index = 5, middle_index = 4
a = 60, arr[middle_index] = 11, begin_index = 5, end_index = 5, middle_index = 5
the result is 6, the index for value nil
[1, 3, 5, 7, 9, 11, 60]
Searching for -60 in [1, 3, 5, 7, 9]
a = -60, arr[middle_index] = 5, begin_index = 0, end_index = 4, middle_index = 2
a = -60, arr[middle_index] = 1, begin_index = 0, end_index = 1, middle_index = 0
a = -60, arr[middle_index] = 9, begin_index = 0, end_index = -1, middle_index = -1
the result is 0, the index for value 1
[-60, 1, 3, 5, 7, 9]
Searching for -60 in [1, 3, 5, 7, 9, 11]
a = -60, arr[middle_index] = 5, begin_index = 0, end_index = 5, middle_index = 2
a = -60, arr[middle_index] = 1, begin_index = 0, end_index = 1, middle_index = 0
a = -60, arr[middle_index] = 11, begin_index = 0, end_index = -1, middle_index = -1
the result is 0, the index for value 1
[-60, 1, 3, 5, 7, 9, 11]
Searching for -60 in [1]
a = -60, arr[middle_index] = 1, begin_index = 0, end_index = 0, middle_index = 0
the result is 0, the index for value 1
[-60, 1]
Searching for 60 in [1]
a = 60, arr[middle_index] = 1, begin_index = 0, end_index = 0, middle_index = 0
the result is 1, the index for value nil
[1, 60]
Searching for 60 in []
[60]
This is the code from Java's java.util.Arrays.binarySearch as included in Oracles Java:
/**
* Searches the specified array of ints for the specified value using the
* binary search algorithm. The array must be sorted (as
* by the {#link #sort(int[])} method) prior to making this call. If it
* is not sorted, the results are undefined. If the array contains
* multiple elements with the specified value, there is no guarantee which
* one will be found.
*
* #param a the array to be searched
* #param key the value to be searched for
* #return index of the search key, if it is contained in the array;
* otherwise, <tt>(-(<i>insertion point</i>) - 1)</tt>. The
* <i>insertion point</i> is defined as the point at which the
* key would be inserted into the array: the index of the first
* element greater than the key, or <tt>a.length</tt> if all
* elements in the array are less than the specified key. Note
* that this guarantees that the return value will be >= 0 if
* and only if the key is found.
*/
public static int binarySearch(int[] a, int key) {
return binarySearch0(a, 0, a.length, key);
}
// Like public version, but without range checks.
private static int binarySearch0(int[] a, int fromIndex, int toIndex,
int key) {
int low = fromIndex;
int high = toIndex - 1;
while (low <= high) {
int mid = (low + high) >>> 1;
int midVal = a[mid];
if (midVal < key)
low = mid + 1;
else if (midVal > key)
high = mid - 1;
else
return mid; // key found
}
return -(low + 1); // key not found.
}
The algorithm has proven to be appropriate and I like the fact, that you instantly know from the result whether it is an exact match or a hint on the insertion point.
This is how I would translate this into ruby:
# Inserts the specified value into the specified array using the binary
# search algorithm. The array must be sorted prior to making this call.
# If it is not sorted, the results are undefined. If the array contains
# multiple elements with the specified value, there is no guarantee
# which one will be found.
#
# #param [Array] array the ordered array into which value should be inserted
# #param [Object] value the value to insert
# #param [Fixnum|Bignum] from_index ordered sub-array starts at
# #param [Fixnum|Bignum] to_index ordered sub-array ends the field before
# #return [Array] the resulting array
def self.insert(array, value, from_index=0, to_index=array.length)
array.insert insertion_point(array, value, from_index, to_index), value
end
# Searches the specified array for an insertion point ot the specified value
# using the binary search algorithm. The array must be sorted prior to making
# this call. If it is not sorted, the results are undefined. If the array
# contains multiple elements with the specified value, there is no guarantee
# which one will be found.
#
# #param [Array] array the ordered array into which value should be inserted
# #param [Object] value the value to insert
# #param [Fixnum|Bignum] from_index ordered sub-array starts at
# #param [Fixnum|Bignum] to_index ordered sub-array ends the field before
# #return [Fixnum|Bignum] the position where value should be inserted
def self.insertion_point(array, value, from_index=0, to_index=array.length)
raise(ArgumentError, 'Invalid Range') if from_index < 0 || from_index > array.length || from_index > to_index || to_index > array.length
binary_search = _binary_search(array, value, from_index, to_index)
if binary_search < 0
-(binary_search + 1)
else
binary_search
end
end
# Searches the specified array for the specified value using the binary
# search algorithm. The array must be sorted prior to making this call.
# If it is not sorted, the results are undefined. If the array contains
# multiple elements with the specified value, there is no guarantee which
# one will be found.
#
# #param [Array] array the ordered array in which the value should be searched
# #param [Object] value the value to search for
# #param [Fixnum|Bignum] from_index ordered sub-array starts at
# #param [Fixnum|Bignum] to_index ordered sub-array ends the field before
# #return [Fixnum|Bignum] if > 0 position of value, otherwise -(insertion_point + 1)
def self.binary_search(array, value, from_index=0, to_index=array.length)
raise(ArgumentError, 'Invalid Range') if from_index < 0 || from_index > array.length || from_index > to_index || to_index > array.length
_binary_search(array, value, from_index, to_index)
end
private
# Like binary_search, but without range checks.
#
# #param [Array] array the ordered array in which the value should be searched
# #param [Object] value the value to search for
# #param [Fixnum|Bignum] from_index ordered sub-array starts at
# #param [Fixnum|Bignum] to_index ordered sub-array ends the field before
# #return [Fixnum|Bignum] if > 0 position of value, otherwise -(insertion_point + 1)
def self._binary_search(array, value, from_index, to_index)
low = from_index
high = to_index - 1
while low <= high do
mid = (low + high) / 2
mid_val = array[mid]
if mid_val < value
low = mid + 1
elsif mid_val > value
high = mid - 1
else
return mid # value found
end
end
-(low + 1) # value not found.
end
Code returns the same values as OP provided for his test data.
Update 2020
Actually, the insertion problem of binary search has been well researched. There is a left insertion point and right insertion point. Code can be found on Wikipedia and Rosetta Code. For example, to find the left insertion point, the code is:
BinarySearch_Left(A[0..N-1], value) {
low = 0
high = N - 1
while (low <= high) {
// invariants: value > A[i] for all i < low
value <= A[i] for all i > high
mid = (low + high) / 2
if (A[mid] >= value)
high = mid - 1
else
low = mid + 1
}
return low
}
One note is about the overflow bug, so mid really should be found as low + floor((high - low) / 2).
Earlier answer:
Actually, instead of checking for begin_index >= end_index, it can be better handled using begin_index > end_index, and the solution is much cleaner:
def binary_search_helper(arr, a, begin_index, end_index)
if begin_index > end_index
return begin_index
else
middle_index = (begin_index + end_index) / 2
if arr[middle_index] == a
return middle_index
elsif a > arr[middle_index]
return binary_search_helper(arr, a, middle_index + 1, end_index)
else
return binary_search_helper(arr, a, begin_index, middle_index - 1)
end
end
end
# for [1,3,5,7,9], searching for 6 will return index for 7 for insertion
# if exact match is found, then return that index
def binary_search(arr, a)
return binary_search_helper(arr, a, 0, arr.length - 1)
end
And using iteration instead of recursion may be faster and have less worry for stack overflow.
sample for left insertion:
def binary_search(arr, target):
left = 0
right = len(arr) - 1
while left <= right:
mid = (left + right) >> 1
if arr[mid] < target:
left = mid + 1
else:
right = mid - 1
return left
sample for right insertion:
def binary_search(arr, target):
left = 0
right = len(arr) - 1
while left <= right:
mid = (left + right) >> 1
if arr[mid] <= target:
left = mid + 1
else:
right = mid - 1
return left # add - 1 for right most index of target
sample for is present:
def binary_search(arr, target):
left = 0
right = len(arr) - 1
while left <= right:
mid = (left + right) >> 1
if n < target:
left = mid + 1
elif n > target:
right = mid - 1
else:
return True # or return mid for index
return False # or return -1 for not found
sample test case:
arr = [1, 2, 3, 4, 5, 5, 5, 5, 5, 5, 5, 6, 7, 8, 9, 10]
result = binary_search(arr, 5)

Resources