binary search

топ 100 блогов _winnie30.04.2015 В книге "жемчужины программирования" сообщается, что невозможно написать binary search с первого раза.

На самом деле его невозможно написать с первого раза, если не осознаешь что такое инвариант цикла. Если осознаёшь - то скорее всего запросто напишешь (я вот уже в третий раз за год написал).

Если не осознаёшь инвариант цикла, то с большой вероятностью будет одна из ошибок:

* mid = (lo+hi)/2 или hi/2+lo/2 ? (и нет, дело не в overflow, эти формулы просто разные (1+3)/2 != 1/2 + 3/2 ).
* индексация за пределами массива (начинаем проверять элемент array[-1]).
* вечное зацикливание, когда на маленьком массиве середина посчитанная с ±1 ошибкой - совпадает с концом или началом массива (массив длины 2 остается массивом длины 2).
* неправильный выбор элемента для сравнения, array[mid] или array[mid-1]?
* неправильный выбор между < и <=
* отсутствие осознания, что найдётся, когда есть несколько элементов равных искомому, или ни одного

Как писать двоичный поиск чтобы он не бажил:
* Выбрать инвариант "если искомый элемент существует, то он в [lo, hi)", изначально выбрать lo,hi = 0, N.
* mid = lo+(hi-lo)/2 (при этом для кода верно утверждение "промежуток длиннее 1 всегда укоротится").
* Если ищем первый среди равных - то сравнение выглядит как key<=array[mid-1], если последний - key
* в зависимости от сравнения присваиваем mid в lo или hi (в любом случае, не mid-1).



Двоичный поиск для равномерно распределённых ключей (хешей) ускоряется простым хаком "для каждого из 65536 двубайтовых префиксов запомнить, где он начинается", это убирает 16 начальных итераций.
Вместо 16 можно выбрать что-то другое, исходя из количества RAM и размера массива. Если не жалко оперативной памяти, а элементы совсем рандомные (поэтому легко посчитать, какой длины должны быть префикс, чтобы на каждый было примерно по 5 хешей) - то можно таблицу префиксов удлинить так, что вместо двоичного поиска можно вставить линейный.
Таблица считается таким кодом:

# на 1 длиннее, чем количество префиксов,
# чтобы всегда был "следующий" для T[high_bits(k) + 1]

T = [None] * (2**K + 1)
j = 0
for i in xrange(2**K):
    T[i] = j
    while j < N and high_bits(K, array[j]) == i:
         j += 1
T[2**K] = N

При поиске ключа k - ищем не в [0, N), а в [T[high_bits(k)], T[high_bits(k) + 1])

Для равномерно распределённых хешей есть ещё
интерполяционный поиск, но я привык к табличке, она кажется проще чем корректная реализация интерполяционного поиска. Например про код в ru/en вики я не уверен, что он без зависаний обрабатывает случайные/злонамеренные отклонения от равномерности. Когда я думаю как этот код сделать надёжным, то думается про double-арифметику с квадратными корнями, для поиска интервалов которые "скорее всего" содержат искомый элемент, с удвоением размера интервала, или препроцессинг "максимальное отклонение искомого элемента от расчетной позиции".

Оставить комментарий

Архив записей в блогах:
С чувством глубокого удовлетворения наблюдал за тем как исходили на говно правители РФ и вся казенная пропаганда. Президент США обслюнявил Зеленского, ему пожали ручку все лидеры стран НАТО... Как в свое время Путину. Представляю как пропагандисты сейчас локти кусают - как можно было ...
В одном из постов Попадосс написал, что у него было 7 лет чудесной жизни в путешествиях. Тон фразы подсказал внимательному читателю, что эти годы закончились. Да и факты налицо - зимовки в Таиланде сначала превратились в пару месячный отдых, потом и вовсе были заменены Сочами. А ...
Юго-Запад Санкт-Петербурга жители города не очень-то жалуют: множество новостроек, большое количество людей, редкие парки... Сюда можно ехать, разве что, посмотреть на залив - это действительно красивое место. Закаты на берегу необыкновенно красивые. Но пару дней назад здесь появилась н ...
Суд признал экстремистской организацию «Домой в СССР », призывавшую вернуться в Советский Союз путем военного переворота. Деятельность сообщества теперь запрещена на территории всей России. Соответствующее решение суда Республики Коми вступило в силу. Сотрудники ФСБ утверждают, что ...
В рубрике #цветыпопонедельникам - ирисы. Две клумбы с этими дивного оттенка цветами появились в одном из скверов нашего микрорайона. Я случайно о них узнала, и специально сходила туда в выходные полюбоваться цветами. ...