Рубрики
Без рубрики

Итеративные против рекурсивных двоичных алгоритмов поиска в Python

Каждый кодер должен знать эти алгоритмы. Узнайте о наилучших способах поиска значений в отсортированном массиве эффективно!

Автор оригинала: Chris.

В этой статье вы узнаете о базовом алгоритме, каждый компьютерный ученый должен знать: Двоичный поиск алгоритм Отказ

Если вы просто хотите попробовать алгоритм в Python, не стесняйтесь играть с нашей интерактивной Python Shell:

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

Основы

Например, скажем, вы хотите найти отсортированный список для значения 56. Наизывный алгоритм начинается с элемента первого списка, проверяет, равно ли это значение 56, и перемещается на следующий элемент списка – пока алгоритм не посетит все элементы. В худшем случае (значение поиска не в списке), алгоритм проходит все элементы списка. Например, поиск отсортированного списка с 10 000 элементов потребуется около 10 000 операций для проверки каждого элемента списка для равенства с помощью поиска. На алгоритмической теории языка мы говорим, что сложность времени выполнения линейна в количестве элементов списка. Это ни в коем случае не оптимально – потому что алгоритм не использует всю доступную информацию для достижения максимальной эффективности. В конце концов, список отсортирован! Используя этот факт, мы можем создать алгоритм, который «касается» только несколько элементов в списке и по-прежнему знает с абсолютной уверенностью, независимо от того, существует ли элемент в списке или нет. На самом деле, вместо того, чтобы пересекать все элементы списка элементов данного отсортированного списка, двоичный алгоритм поиска проходит только элементы log2 (n) (логарифм базы 2). Другими словами, мы можем найти тот же список 10 000 элементов, используя только Log2 (10000) <14 вместо 10 000 операций!

Как найти список в логарифмическом времени выполнения? Самый популярный алгоритм, который решает эту проблему, является алгоритм двоичного поиска. Идея проста: предположим, что список сортируется восходящим образом. Алгоритм начинает проверять средний элемент сначала. Если наше значение искали меньше, чем этот средний элемент, мы знаем, что все элементы между серединой и элементами последнего списка больше, чем значение поиска (из-за сортированного свойства). Исканный элемент не будет существовать в этой половине списка, поэтому мы можем немедленно отклонить половину элементов списка с одной операцией. Точно так же, если значение поиска больше среднего элемента, мы можем отклонить первую половину элементов списка. Теперь мы просто повторяем эту процедуру – добавление эффективного списка размером элементов для проверки на каждом этапе алгоритма. Вот визуальный пример:

Рисунок: Пример алгоритма двоичного поиска.

На рисунке показан двоичный алгоритм поиска на работе. Скажем, вы хотите найти значение 56 в отсортированном списке из восьми целочисленных целей. Рекомендайте, что наша цель – пройти отсортированный список в логарифмическом времени, поэтому мы не можем позволить себе прикоснуться к каждому элементу в списке. Двоичный алгоритм поиска в графике несколько раз прослеживает элемент X в середине списка (округление вниз). Есть три случая:

  1. Элемент х больше, чем искомое значение 56. В этом случае алгоритм игнорирует правильную часть списка, так как все элементы больше 56, потому что список уже отсортирован.
  2. Элемент X меньше, чем ищенное значение 56. Это То, что Мы наблюдаем на рисунке. Здесь алгоритм игнорирует левую часть списка, так как они меньше, а также, используя свойство, которое список уже отсортирован).
  3. Элемент х равен поисков значению 56. Вы можете увидеть этот случай в последней строке на рисунке. Поздравляем, вы только что нашли поиск элемент в списке!

В каждом этапе алгоритма пространство поиска сводится к половине. Это означает, что после логарифмического числа шагов мы нашли элемент! Вот практическая реализация двоичного алгоритма поиска:

def binary_search(lst, value):
    lo, hi = 0, len(lst)-1
    while lo <= hi:
        mid = (lo + hi) // 2
        if lst[mid] < value:
            lo = mid + 1
        elif value < lst[mid]:
            hi = mid - 1
        else:
            return mid
    return -1

    
l = [3, 6, 14, 16, 33, 55, 56, 89]
x = 56
print(binary_search(l,x))
# 6 (the index of the found element)

Листинг: итеративный двоичный алгоритм поиска.

Алгоритм принимает в качестве аргументов списка и значение для поиска. Затем он неоднократно половиняет пространство поиска, используя две переменные LO и HI. Эти переменные определяют интервал возможных элементов списка, где может существовать поисковое значение. Предыдущая переменная LO определяет индекс START и последняя переменная Hi определяет конечный индекс интервала. Мы неоднократно проверяем, в каком случае в случае вышеуказанных случаев средний элемент падает и адаптирует интервал потенциальных элементов соответственно, изменяя значения LO и HI, как описано выше. Хотя этот алгоритм является совершенно действительным, читабельным и эффективным реализацией двоичного алгоритма поиска, это не однонаправочное решение!

Рекурсивный двоичный алгоритм поиска

Проблема: внедрить алгоритм двоичного поиска в одной строке кода!

## The Data
l = [3, 6, 14, 16, 33, 55, 56, 89]
x = 33

## The One-Liner
bs = lambda l, x, lo=0, hi=len(l)-1: -1 if lo>hi else \
         (lo+hi)//2 if l[(lo+hi)//2] == x \
         else bs(l, x, lo, (lo+hi)//2-1) if l[(lo+hi)//2] > x \
         else bs(l, x, (lo+hi)//2+1, hi)


## The Results
print(bs(l, x))

Листинг: одноклассное решение с использованием базового арифметика массива.

Угадай вывод этого кода-фрагмента!

Как это работает

Для читабельности я сломал этот раствор «одноклассника» на четыре строки – даже если вы можете написать его в одной строке кода. Часто лучше ограничить длину одной строки, потому что для читателей облегчает понять код.

Я использовал рекурсивный способ определения алгоритма двоичного поиска.

(I) Мы создаем новую функцию BS с помощью оператора Lambda с четырьмя аргументами: l, x, lo и hi. Первые два аргумента L и X определяют сортированный список и значение, на котором можно найти. Последние два аргумента HI и LO определяют минимальный и максимальный индекс текущего сублиста, который будет искать значение x. На каждом уровне рекурсии мы рассматриваем подсудист (как указано индексами привет и низким), которое становится меньше и меньше, увеличивая индекс низкого и уменьшающегося индекса. После конечного числа шагов состояние низкого уровня> HI верно. Это базовый случай нашей рекурсии, и если мы не нашли поисковый элемент X к настоящему времени, мы возвращаем -1, указывающее, что такой элемент не существует.

(Ii) Мы возвращаем индекс (LO + HI)//2 среднего элемента (в указанном подсудистом), если этот элемент является поиском значения. Обратите внимание, что мы используем целочисленное разделение для раунда до следующего целочисленного значения, которое можно использовать как Список показатель.

(III) Однако, если средний элемент больше, чем значение искали, нет необходимости искать все элементы справа от среднего элемента. Эти элементы тоже будут больше, потому что список отсортирован. Следовательно, мы вызываем функцию рекурсивно, но адаптируйте Привет индекс Рассмотреть только элементы списка слева от среднего элемента.

(Iv) Аналогично, если средний элемент меньше, чем искали значение, нет необходимости искать все элементы слева от среднего элемента. Следовательно, мы вызываем функцию рекурсивно, но адаптируйте индекс LO, чтобы рассмотреть только элементы списка справа от среднего элемента.

Таким образом, при поиске значения 33 в списке [3, 6, 14, 16, 33, 55, 56, 89] результатом является индекс 4.

Я надеюсь, что эта статья улучшила ваш общий код понимания навыков понимания различных функций Python, таких как условное исполнение, основные ключевые слова, арифметические операции и важная тема индексации программной последовательности. Но что более важно, вы узнали, как использовать рекурсию, чтобы облегчить сложные проблемы.

Куда пойти отсюда?

Многие кодеры никогда не чувствуют, что они не узнали достаточно, чтобы применить свои навыки в реальном мире. Это огромная ошибка. Не будь одним из тех кодеров – потому что это самореализация пророчества! Они застряли изучать игрушечные проекты навсегда и никогда не будут успешными.

Вместо этого поставим себя на четкий путь к овладению с строгим планом обучения 70/30: 70% практических кодовых проектов и теории 30%. Многие студенты уже следовали этот план обучения – и ускорили свой уровень квалификации, как ничего раньше. Создайте свой новый навык с высоким доходом Python и достигните уровня Freelancer Python в течение нескольких месяцев! Ссылка приводит вас к моей углубленной программе Python для предстоящих фрилансеров и профессионалов Python.

Работая в качестве исследователя в распределенных системах, доктор Кристиан Майер нашел свою любовь к учению студентов компьютерных наук.

Чтобы помочь студентам достичь более высоких уровней успеха Python, он основал сайт программирования образования Finxter.com Отказ Он автор популярной книги программирования Python одноклассники (Nostarch 2020), Coauthor of Кофе-брейк Python Серия самооставленных книг, энтузиаста компьютерных наук, Фрилансера и владелец одного из лучших 10 крупнейших Питон блоги по всему миру.

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

Оригинал: “https://blog.finxter.com/iterative-vs-recursive-binary-search-algorithms-in-python/”