Целочисленный двоичный поиск — различия между версиями
Alex700 (обсуждение | вклад) |
|||
Строка 27: | Строка 27: | ||
== Алгоритм двоичного поиска == | == Алгоритм двоичного поиска == | ||
− | [[Файл:shcemebinsearch.png|350px|thumb|right|Схема | + | [[Файл:shcemebinsearch.png|350px|thumb|right|Схема бинарного поиска]] |
Идея поиска заключается в том, чтобы брать элемент посередине, между границами, и сравнивать его с искомым. | Идея поиска заключается в том, чтобы брать элемент посередине, между границами, и сравнивать его с искомым. | ||
− | + | Если искомое больше(в случае правостороннего {{---}} не меньше), чем элемент сравнения, | |
− | то сужаем область поиска так, чтобы новая левая граница была равна индексу середины предыдущей области. В противном случае присваиваем это значение правой границе. Проделываем эту процедуру до тех пор, пока правая граница больше левой более чем на <tex>1</tex>, | + | то сужаем область поиска так, чтобы новая левая граница была равна индексу середины предыдущей области. В противном случае присваиваем это значение правой границе. Проделываем эту процедуру до тех пор, пока правая граница больше левой более чем на <tex>1</tex>. В случае правостороннего бинарного поиска ответом будет индекс <tex>l</tex>, а в случае левостороннего {{---}} <tex>r</tex>. |
== Код == | == Код == | ||
Строка 48: | Строка 48: | ||
Инвариант цикла: пусть левый индекс не больше искомого элемента, а правый {{---}} строго больше, тогда если <tex>l = r - 1</tex>, то понятно, что <tex>l</tex> {{---}} самое правое вхождение (так как следующее уже больше). | Инвариант цикла: пусть левый индекс не больше искомого элемента, а правый {{---}} строго больше, тогда если <tex>l = r - 1</tex>, то понятно, что <tex>l</tex> {{---}} самое правое вхождение (так как следующее уже больше). | ||
+ | |||
+ | == Несколько слов об эвристиках == | ||
+ | |||
+ | '''Эвристика с завершением поиска, при досрочном нахождении искомого элемента''' | ||
+ | |||
+ | Заметим, что если нам необходимо просто проверить наличие элемента в упорядоченном множестве, то можно использовать любой из правостороннего и левостороннего поиска. | ||
+ | При этом давайте будем на каждой итерации проверять "не попали ли мы в элемент, равный искомому", и в случае попадания заканчивать поиск. | ||
+ | |||
+ | '''Эвристика с запоминанием ответа на предыдущий запрос''' | ||
+ | |||
+ | Пусть дан отсортированный массив чисел, упорядоченных по неубыванию. | ||
+ | Также пусть запросы приходят в таком порядке, что каждый следующий не меньше, чем предыдущий. | ||
+ | Для ответа на запрос будем использовать левосторонний двоичный поиск. | ||
+ | При этом после того как обработался первый запрос, запомним чему равно <tex>l</tex>, запишем его в переменную <tex>start\_l</tex>. | ||
+ | Когда будем обрабатывать следующий запрос, то проинициализируем левую границу как <tex>start\_l</tex>. | ||
+ | Заметим, что все элементы, которые лежат не правее <tex>start\_l</tex>, строго меньше текущего искомого элемента, так как они меньше предыдущего запроса, а значит и меньше текущего. Значит инвариант цикла выполнен. | ||
== См. также == | == См. также == |
Версия 22:44, 15 июня 2014
Целочисленный двоичный поиск (бинарный поиск) (англ. binary search) — алгоритм поиска объекта по заданному признаку в множестве объектов, упорядоченных по тому же самому признаку, работающий за логарифмическое время.
Содержание
[убрать]Формулировка задачи
Пусть нам дан упорядоченный массив, состоящий только из целочисленных элементов. Требуется найти позицию, на которой находится заданный элемент. Для этой задачи мы и можем использовать двоичный поиск.
Принцип работы
Двоичный поиск заключается в том, что на каждом шаге множество объектов делится на две части и в работе остаётся та часть множества, где находится искомый объект. Или же, в зависимости от постановки задачи, мы можем остановить процесс, когда мы получим первый или же последний индекс вхождения элемента. Последнее условие — это левосторонний/правосторонний двоичный поиск.
Правосторонний/левосторонний целочисленный двоичный поиск
Для простоты дальнейших определений будем считать, что
и что .
Определение: |
Правосторонний бинарный поиск (англ. rightside binary search) — бинарный поиск, с помощью которого мы ищем | , где — массив, а — искомый ключ
Определение: |
Левосторонний бинарный поиск (англ. leftside binary search) — бинарный поиск, с помощью которого мы ищем | , где — массив, а — искомый ключ
Использовав эти два вида двоичного поиска, мы можем найти интервал позиций таких, что и
Например:
Задан отсортированный массив
Правосторонний поиск двойки выдаст в результате
, в то время как левосторонний выдаст .От сюда следует, что количество подряд идущих двоек равно длине отрезка
, то есть .Если искомого элемента в массиве нет, то правосторонний поиск выдаст минимальный элемент, больший искомого, а левосторонний наоборот, максимальный элемент, меньший искомого.
Алгоритм двоичного поиска
Идея поиска заключается в том, чтобы брать элемент посередине, между границами, и сравнивать его с искомым. Если искомое больше(в случае правостороннего — не меньше), чем элемент сравнения, то сужаем область поиска так, чтобы новая левая граница была равна индексу середины предыдущей области. В противном случае присваиваем это значение правой границе. Проделываем эту процедуру до тех пор, пока правая граница больше левой более чем на
. В случае правостороннего бинарного поиска ответом будет индекс , а в случае левостороннего — .Код
binSearch(Object[] a, Object key) // l, r - левая и правая границы int l = 0 int r = len(a) + 1 while l < r - 1 // запускаем цикл m = (l + r) / 2 // m - середина области поиска if a[m] < key l = m else r = m // сужение границ return r
В случае правостороннего поиска изменится знак сравнения при сужении границ на .
Инвариант цикла: пусть левый индекс не больше искомого элемента, а правый — строго больше, тогда если
, то понятно, что — самое правое вхождение (так как следующее уже больше).Несколько слов об эвристиках
Эвристика с завершением поиска, при досрочном нахождении искомого элемента
Заметим, что если нам необходимо просто проверить наличие элемента в упорядоченном множестве, то можно использовать любой из правостороннего и левостороннего поиска. При этом давайте будем на каждой итерации проверять "не попали ли мы в элемент, равный искомому", и в случае попадания заканчивать поиск.
Эвристика с запоминанием ответа на предыдущий запрос
Пусть дан отсортированный массив чисел, упорядоченных по неубыванию. Также пусть запросы приходят в таком порядке, что каждый следующий не меньше, чем предыдущий. Для ответа на запрос будем использовать левосторонний двоичный поиск. При этом после того как обработался первый запрос, запомним чему равно
, запишем его в переменную . Когда будем обрабатывать следующий запрос, то проинициализируем левую границу как . Заметим, что все элементы, которые лежат не правее , строго меньше текущего искомого элемента, так как они меньше предыдущего запроса, а значит и меньше текущего. Значит инвариант цикла выполнен.См. также
Источники информации
- Д. Кнут - Искусство программирования (Том 3, 2-е издание)
- Википедия - двоичный поиск
- Интересная статья про типичные ошибки