Ruby: bsearch возвращает ноль

#ruby #ruby-on-rails-4 #binary-search

#ruby #ruby-on-rails-4 #двоичный поиск

Вопрос:

У меня есть отсортированный массив number, и я хотел проверить, есть ли number в массиве или нет. Я думаю, что я должен использовать bsearch здесь, но он возвращает nil , даже если число присутствует в массиве, и дает положительный результат только для среднего элемента.

Вот что я тестирую.

 >> [1,2,3,4,5].bsearch { |value| value <=> 1}
>> nil
>> [1,2,3,4,5].bsearch { |value| value <=> 3}
>> 3
  

Я использую Ruby версии 2.3.8

Ответ №1:

Вы используете Array#bsearch в режиме поиска любого.

Как говорится в документации, ваш блок должен удовлетворять следующим требованиям:

  • Все элементы с положительной оценкой предшествуют всем элементам с нулевой оценкой.
  • Все элементы с положительной оценкой предшествуют всем элементам с отрицательной оценкой.
  • Все элементы с нулевой оценкой предшествуют всем элементам с отрицательной оценкой.

Ваш блок нарушает эти требования.

Давайте рассмотрим пример. На первой итерации Array#bsearch вы выберете средний элемент, который есть 3 , и передадите его вашему блоку. Возвращается ваш блок 1 , что означает, что элемент, который вы ищете, больше 3 и, следовательно, должен быть справа от текущего элемента. Итак, Array#bsearch отбрасывается левая половина массива, и мы остаемся [4, 5] . Теперь снова Array#bsearch выберите «средний» элемент, который есть 4 , и передайте его вашему блоку. Ваш блок возвращает 1 положительное число и, таким образом, означает, что искомый элемент больше 4 и, следовательно, должен находиться в правой половине массива. Итак, снова Array#bsearch отбрасываем левую половину, и мы остаемся [5] . То же самое, Array#bsearch переходит 5 к блоку, ваш блок возвращает 1 положительное число и, таким образом, означает, что нам нужно посмотреть вправо, за исключением того, что мы уже находимся в конце массива, и, таким образом, мы можем сделать вывод, что элемента, который вы ищете, нет в массиве.

Итак, по сути, вы просто говорите Array#bsearch искать не в том месте.

Давайте еще раз посмотрим на документацию:

Они имеют смысл как блоки в режиме поиска любого:

 a = [0, 4, 7, 10, 12]
a.map {|element| 7 <=> element } # => [1, 1, 0, -1, -1]
a.map {|element| -1 <=> element } # => [-1, -1, -1, -1, -1]
a.map {|element| 5 <=> element } # => [1, 1, -1, -1, -1]
a.map {|element| 15 <=> element } # => [1, 1, 1, 1, 1]
  

Это не имело бы смысла:

 a = [0, 4, 7, 10, 12]
a.map {|element| element <=> 7 } # => [-1, -1, 0, 1, 1]
  

Итак, по сути, вы написали свой блок точно так же, как блок, который, как явно указано в документации, является неправильным, и в точности противоположным тому, что указано в документации.

Если вы перевернете два операнда в блоке, чтобы ваш блок выглядел так, как указано в примере в документации, он должен выглядеть, он будет работать:

 [1, 2, 3, 4, 5].bsearch { |value| 0 <=> value } #=> nil
[1, 2, 3, 4, 5].bsearch { |value| 1 <=> value } #=> 1
[1, 2, 3, 4, 5].bsearch { |value| 2 <=> value } #=> 2
[1, 2, 3, 4, 5].bsearch { |value| 3 <=> value } #=> 3
[1, 2, 3, 4, 5].bsearch { |value| 4 <=> value } #=> 4
[1, 2, 3, 4, 5].bsearch { |value| 5 <=> value } #=> 5
[1, 2, 3, 4, 5].bsearch { |value| 6 <=> value } #=> nil