Алгоритмы поиска
Last updated
Last updated
Мы постоянно что-то ищем с помощью компьютера — номера телефонов, свободные номера в гостиницах, товары в интернет-магазинах, квартиры в аренду. За каждым из этих поисков скрываются разные алгоритмы. Среди них — поиск подстроки, поиск по ключевым словам, префиксный поиск. Мы познакомимся с двумя алгоритмами: методом перебора и бинарным поиском
Он же линейный (последовательный) поиск — проверка всех значений в списке с начала и до нужного атрибута. Выделяют два алгоритма перебора — поиск по списку и поиск по ключу
Поиск по списку: если нам нужно найти слово в списке слов — мы последовательно будем проверять все слова, пока не найдём нужное
Поиск по ключу:
Допустим у нас есть список городов с датами основания. Мы знаем год основания города и хотим найти его название. Так, мы знаем один атрибут города — год основания, а хотим найти другой атрибут — название города. Известный нам атрибут называется ключом, по которому мы проводим поиск
Интерактив: попробуйте поразмышлять — какое действие сильно облегчило бы подобный поиск по ключу
Из примеров видно, что метод перебора работает достаточно медленно — в худшем случае нам нужно будет проверить все значения в списке.
Представим, что по такому методу работает проверка логина и пароля в Facebook. Массив с логинами-паролями всех пользователей содержит около трех миллиардов элементов и даже на самых быстрых серверах этот процесс занял бы несколько минут
Самый простой способ — упорядочить данные, т.е. отсортировать их. Представьте, если бы в телефонном справочнике имена и фамилии не были бы отсортированы, сколько бы занял поиск номера. А если они отсортированы по алфавиту, мы сразу можем "отсечь" часть списка, в котором искомой фамилии точно нет.
Это метод поиска, при котором алгоритм ищет элементы в ограниченной области поиска, причем с каждым шагом область поиска делится на две части.
Механизм работы:
Бинарный поиск начинается со среднего элемента и на первом шаге идёт по всему массиву
На каждом шаге область поиска делится пополам, при этом границы поиска задаются первым и последним элементом
Алгоритм завершается, когда область поиска сужается до одного элемента
Необходимое условие для применения бинарного поиска — массив должен быть отсортирован.
Отсортируем массив городов по алфавиту:
Изначально область поиска — весь массив.
Делим его пополам, в середине элемент "Киев"
, сравниваем его с искомым — таким образом сужаем область поиска до половины массива. И далее делим пополам уже оставшуюся область поиска.
Практика:
Составьте псевдокод для программы поиска города в списке городов из примера выше.
Посмотрим на реализацию в коде:
Мы рассмотрели линейный и бинарный поиск на примере списка городов. Обратим внимание на ограничения использования бинарного поиска:
Бинарный поиск сложнее в реализации. Если у нас массив более 100 элементов имеет смысл воспользоваться бинарным поиском, если массив небольшой — достаточно метода перебора, он будет работать со сравнимой скоростью или даже быстрее
Массив для бинарного поиска должен быть упорядоченным. Это не проблема, если массив фиксированный, т.е. данные не меняются по ходу работы программы, а подготавливаются один раз. Но если данные нужно добавлять — придётся писать дополнительный сложный код.
Существуют данные, которые невозможно упорядочить (например, цвета или пары чисел — координаты: широта и долгота)
Бинарный поиск быстрее линейного, но насколько? Как это измерить?
Мы можем измерить время работы программ. Но это время сильно зависит от конкретной машины и условий (фоновые задачи и т.п.), а поэтому не является объективным.
Оценим скорость теоретически — быстрее тот алгоритм, в котором совершается меньше операций. В обоих алгоритмах операции проходят в цикле и для объективности будем считать среднее количество циклов
Мы рассмотрели два алгоритма: метод перебора и бинарный поиск
У метода перебора есть два варианта:
Простой перебор, чтобы проверить, есть ли элемент в списке
Поиск по ключу, если нужно найти элемент по одному атрибуту
Бинарный поиск позволяет искать элементы в упорядоченном списке. На каждом шаге алгоритма область поиска делится на две части.
Бинарный поиск гораздо быстрее обычного поиска, но при этом сложнее в реализации
Поиск максимального элемента: Дан массив чисел. Найдите максимальный элемент в этом массиве, используя линейный поиск.
Поиск первого вхождения элемента: Дан массив строк и ключевое слово. Найдите индекс первого вхождения ключевого слова в массиве, используя линейный поиск.
Поиск элемента в отсортированном массиве: Дан отсортированный массив чисел и элемент этого массива. Найдите индекс элемента, используя бинарный поиск.
Как понять, насколько быстро работает алгоритм?
Попробуем рассчитать время выполнения алгоритма. Допустим, время выполнения одной операции составляет 1 мс. Одной операцией для поиска является одно прохождение по массиву. Для линейного поиска в массиве из 100 элементов время составит 100 мс (в худшем случае). Для бинарного поиска — 7 мс. Посчитайте, сколько времени понадобится для линейного и бинарного поиска в массиве из 1000 элементов
Мы видим, что с увеличением размера массива время выполнения линейного и бинарного поиска увеличивается, но с существенно разной скоростью
Таким образом, для линейного поиска по массиву длиной N элементов нужно в худшем случае нужно произвести N операций. Для бинарного поиска — log N операций — степень числа 2, в которую нужно её возвести, чтобы получить N
Для того, чтобы понимать, насколько быстро или медленно работают алгоритмы используют нотацию O ("О-большое"). С её использованием формулировка будет выглядеть так — скорость работы алгоритма линейного поиска составляет O(n), а скорость работы бинарного поиска составляет O(log n)
О-большое позволяет сравнить количество операций — именно так принято сравнивать скорость работы алгоритмов. Чаще всего используют худший случай, т.е. максимальное возможное количество операций для алгоритма, но иногда учитывают и среднее время выполнения
Типичные примеры О-большого:
O(log n), или логарифмическое время. Пример: бинарный поиск.
O(n), или линейное время. Пример: простой поиск.
O(n*log n). Пример: эффективные алгоритмы сортировки
O(n2). Пример: медленные алгоритмы сортировки
O(n!). Пример: очень медленные алгоритмы (задача о коммивояжёре)
Практика:
Приведите время выполнения "О-большое" для каждого из следующих сценариев:
Известная фамилия, нужно найти номер в телефонной книге
Известен номер, нужно найти фамилию в телефонной книге