Что такое поиск минимального поиска Ruby и поиск любого поведения?

Я читаю документацию по bsearch Ruby.

Вроде когда блок возвращает либо истину, либо ложь, то bsearch работает в режиме "найти-минимум". И есть ли режим поиска максимума?

Я не совсем понимаю минимальное поведение bsearch Ruby для случаев с 3-го по 5-й ниже:

[10, 20, 50, 80, 110].bsearch{|a| a >= 25}
=> 50

[10, 20, 50, 80, 110].bsearch{|a| a >= 20}
=> 20

[10, 20, 50, 80, 110].bsearch{|a| a == 20}
=> nil

[10, 20, 50, 80, 110].bsearch{|a| a < 50}
=> nil

[10, 20, 50, 80, 110].bsearch{|a| a <= 50}
=> 10
  • Что касается третьего случая, почему он не может найти 20?
  • Что касается 4-го случая, почему он также не может найти 20? (первый менее 50).
  • В пятом случае, почему он находит 10, а не 20?

Кроме того, кажется, что bsearch будет использовать режим find-any, когда блок не возвращает true или false, а возвращает число. Но я не могу понять, что он делал в документах:

ary = [0, 4, 7, 10, 12]
# try to find v such that 4 <= v < 8
ary.bsearch {|x| 1 - x / 4 } #=> 4 or 7

например, что такое 1 - x / 4 и что он делает?


person nonopolarity    schedule 29.08.2019    source источник


Ответы (3)


  • Что касается третьего случая, почему он не может найти 20?
  • Что касается 4-го случая, почему он также не может найти 20? (первый менее 50).
  • В пятом случае, почему он находит 10, а не 20?

Ваши примеры 3, 4 и 5 нарушают предварительное условие метода, поэтому метод может делать все, что захочет, включая возврат nil, возврат 10 или форматирование жесткого диска. (Хотя последнее маловероятно.)

В документации указано, что:

блок должен возвращать true или false, и должен быть индекс i (0 ‹= i ‹= ary.size), чтобы:

  • блок возвращает false для любого элемента, индекс которого меньше i, и
  • блок возвращает true для любого элемента, индекс которого больше или равен i.

Ваш блок нарушает это условие, поэтому метод не может вернуть осмысленный результат.

Давайте на самом деле рассмотрим пример запуска Array#bsearch для вашего третьего случая:

[10, 20, 50, 80, 110].bsearch{|a| a == 20 }

На первой итерации алгоритм выберет середину массива (50) и проверит ее на вашем блоке:

50 == 20 #=> false

Значение false означает, что искомый элемент находится справа от текущего элемента. Следовательно, искомый элемент должен находиться в подмассиве [80, 110]. Итак, рекурсивно:

[80, 110].bsearch{|a| a == 20 }

Опять же, мы выбираем «средний» элемент (110) и тестируем его на вашем блоке:

110 == 20 #=> false

Поскольку возвращаемое значение блока равно false, мы знаем, что элемент должен быть справа от текущего элемента, но справа больше нет элемента, следовательно, мы знаем, что элемент, который мы ищет, не существует.

Теперь для вашего четвертого случая:

[10, 20, 50, 80, 110].bsearch{|a| a < 50 }

На первой итерации алгоритм выберет середину массива (50) и проверит ее на вашем блоке:

50 < 50 #=> false

Значение false означает, что искомый элемент находится справа от текущего элемента. Следовательно, искомый элемент должен находиться в подмассиве [80, 110]. Итак, рекурсивно:

[80, 110].bsearch{|a| a < 50 }

Опять же, мы выбираем «средний» элемент (110) и тестируем его на вашем блоке:

110 < 50 #=> false

Поскольку возвращаемое значение блока равно false, мы знаем, что элемент должен быть справа от текущего элемента, но справа больше нет элемента, следовательно, мы знаем, что элемент, который мы ищет, не существует.

Пятый случай:

[10, 20, 50, 80, 110].bsearch{|a| a <= 50 }

На первой итерации алгоритм выберет середину массива (50) и проверит ее на вашем блоке:

50 <= 50 #=> true

Значение true означает, что искомый элемент находится слева от текущего элемента. Следовательно, искомый элемент должен находиться в подмассиве [10, 20]. Итак, рекурсивно:

[10, 20].bsearch{|a| a <= 50 }

Опять же, мы выбираем «средний» элемент (20) и тестируем его на вашем блоке:

20 <= 50 #=> true

Итак, элемент все еще должен находиться в левом подмассиве:

[10].bsearch{|a| a <= 50 }

проверит

10 <= 50 #=> true

Поскольку возвращаемое значение блока равно true, мы знаем, что элемент должен быть либо слева от текущего элемента или этого элемента, но справа больше нет элемента, следовательно, мы знаем что элемент, который мы ищем, является этим элементом.

ПРИМЕЧАНИЕ: я предполагал, что Array#bsearch всегда будет выбирать элемент как можно ближе к середине, и я предполагал, что для четного числа элементов всегда будет выбираться один правильный середины. Но вы знаете, что они говорят о предположениях: это делает нас с вами задницами. И действительно, в документации прямо указано:

Не определено, какое значение фактически выбирается на каждой итерации.

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

например, что такое 1 - x / 4 и что он делает?

Это просто ваш критерий поиска. Это 0 для x == 4, положительное для x < 4 и отрицательное для x > 4. Это именно то, что нужно режиму find-any: положительное значение указывает алгоритму, что он должен смотреть влево, отрицательное значение указывает, что алгоритм должен смотреть вправо, а ноль означает, что "вы нашли диапазон":

блок всегда должен возвращать число, и должно быть два индекса i и j (0 ‹= i ‹= j ‹= ary.size), чтобы:

  • блок возвращает положительное число для ary, если 0 ‹= k ‹ i,
  • блок возвращает ноль для ary, если i ‹= k ‹ j, и
  • блок возвращает отрицательное число для ary, если j ‹= k ‹ ary.size.
person Jörg W Mittag    schedule 29.08.2019
comment
хорошо, кажется, что предоставленный блок не является условием, на которое вы надеетесь. Это условие должно разделить массив на 2 половины. И точкой, такой, что, грубо говоря, слева от этой точки все false, а справа (включая себя) все true. Этот механизм предназначен для реализации бинарного поиска, который всегда имеет ответ, и таким образом блок обеспечивает условие и вписывается в механизм bsearch для разделения пополам и пополам. - person nonopolarity; 29.08.2019
comment
Я пришел к выводу, что существует 2 типа кода двоичного поиска: один, который может не иметь ответа, и код имеет while low <= high и high = mid - 1... и есть еще один тип кода двоичного поиска, тот, который имеет while low < high (это не <=) и часто это high = mid... кажется, что bsearch всегда делает это с типом ответа бинарного поиска (во всяком случае, для режима поиска-минимума).., я не уверен насчет поиска-любого режим еще - person nonopolarity; 29.08.2019
comment
В одной простой фразе: блок, который вы предоставляете, должен разделить пополам, а не указать, что вы хотите. (так что это отличается от array.select(), например) - person nonopolarity; 29.08.2019

В вашем третьем примере вы не выполняете одно из требований для режима поиска минимума, а именно, что блок возвращает true для любого значения, которое больше или равно вашему значению поиска.

В третьем случае вы поменяли местами значения, так что вы возвращаете true для малых значений и false для больших значений, что снова приводит к неопределенному поведению.

Точно так же в четвертом случае вы изменили порядок проверки на равенство. Требование методов состоит в том, что блок возвращает true, если искомое значение больше или равно искомому значению, и false, если оно меньше.

В целом, алгоритм bsearch достаточно эффективен, если вы можете выполнить все его требования, а именно, чтобы искомый массив был отсортирован и вы указали подходящий блок. В этом случае он, вероятно, будет более эффективным, чем основной метод find.

Если вам не хватает какого-либо из его требований, вы получите неопределенное поведение и фактически случайные результаты. В этом случае вы, вероятно, получите лучшие результаты, просто используя Enumerable#find который также работает с массивами, хэшами,...

person Holger Just    schedule 29.08.2019

Несмотря на документацию, Array#bsearch не

находит значение из этого массива, которое удовлетворяет заданному условию

Что именно делает этот метод, так это то, что он требует от вас создания такого массива ary и такого блока blk, что

режим поиска минимума

... код ary.map(&blk) должен возвращать массив вроде

[false, false, ..., false, false, true, true, ..., true, true]
#                                 ^^^^

тогда код ary.bsearch(&blk) вернет вам крайний левый элемент массива, который возвращает true вместо bkl.call(element).

найти любой режим

... код ary.map(&blk) должен возвращать массив вроде

[positive, ..., positive, 0, ..., 0, negative, ..., negative]
#                         ^^^^^^^^^

тогда код ary.bsearch(&blk) вернет вам элемент массива first-met-by-bisect-jump, который возвращает 0 для bkl.call(element).


Случаи 3-5:

  1. [10, 20, 50, 80, 110].bsearch{|a| a == 20}

не имеет смысла как

[10, 20, 50, 80, 110].map{|a| a == 20}
=> [false, true, false, false, false]
  1. [10, 20, 50, 80, 110].bsearch{|a| a < 50}

можно изменить в соответствии с требованиями режима поиска минимума:

[10, 20, 50, 80, 110].reverse.map{|a| a < 50}
=> [false, false, false, true, true]
[10, 20, 50, 80, 110].reverse.bsearch{|a| a < 50}
=> 20
  1. [10, 20, 50, 80, 110].bsearch{|a| a <= 50}

можно было бы использовать и reverse:

[10, 20, 50, 80, 110].reverse.bsearch{|a| a <= 50}
=> 50
person Pavel Mikhailyuk    schedule 29.08.2019
comment
вы упомянули, что случай 4 может быть выполнен с помощью [10, 20, 50, 80, 110].reverse.bsearch{|a| a < 50}, в то время как в реальной ситуации вы, вероятно, не отмените его, потому что это O (n), и если вы используете O (n), вы также можете выполнить линейный поиск. Но, может быть, это просто пример, что если вы используете условие < или <=, массив действительно должен быть в порядке убывания. Если массив в порядке возрастания, то условие в блоке, вообще говоря, либо >, либо >=... - person nonopolarity; 30.08.2019
comment
и если вы хотите, чтобы первый индекс элемента был меньше 50, это будет [10, 20, 50, 80, 110].bsearch_index{|a| a >= 50} - 1 => 1, а если возвращаемое значение равно -1, это означает, что он находится за левым концом массива (или что не существует такого элемента, который меньше 50). Но поскольку такого элемента, который вообще удовлетворяет условию, быть не может, возвращаемое значение bsearch или bsearch_index может быть nil, поэтому полный код должен быть (arr = [10, 20, 50, 80, 110]; arr.bsearch_index{|a| a >= 2000} || arr.length) - 1 - person nonopolarity; 30.08.2019
comment
#reverse был просто демонстрацией условий #bsearch. Конечно, вы должны настроить свой блок в соответствии с вашим фактическим порядком сортировки. - person Pavel Mikhailyuk; 30.08.2019
comment
Я полагаю, документ #bsearch неясен, поэтому я попытался объяснить, каких именно данных он от вас ждет. - person Pavel Mikhailyuk; 30.08.2019