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

Бинарный поиск в Python: визуальное введение

Автор оригинала: Estefania Cassingena Navone.

Добро пожаловать

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

В частности, вы узнаете:

  • Как алгоритм работает за кулисами, чтобы найти целевой элемент.
  • Как его проводка Python реализация работает линия по линии.
  • Почему это очень эффективный алгоритм по сравнению с линейным поиском.
  • Его преимущества и требования.

Давайте начнем! ✨.

🔹 Введение в двоичный поиск

Этот алгоритм используется для поиска элемента в упорядоченной последовательности (например: список, кортеж или строка).

Требования

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

💡 Совет: Вы можете отсортировать последовательность перед применением двоичного поиска со сортировочным алгоритмом, который соответствует вашим потребностям.

Ввод и вывод

Алгоритм (реализован как функция) нуждается в этих данных:

  • Заказанная последовательность элементов (например, список, кортеж, строка).
  • Целевой элемент, который мы ищем.

Это возвращает индекс элемента, который вы ищете, если это найдено. Если элемент не найден, -1 возвращается.

Эффективность

Это очень эффективно по сравнению с линейным поиском (поиск элемента один за другим, начиная с первого), потому что мы можем «отменить» половину списка на каждом шаге.

Давайте начнем погрузиться в этот алгоритм.

🔸 Визуальный прохождение

Мы применим алгоритм двоичного поиска в этот список:

💡 Совет: Обратите внимание, что список уже отсортирован. Он включал в себя индексы как визуальную ссылку.

Цель

Мы хотим найти индекс целого числа 67 Отказ

Интервал

Давайте притворяться, что мы – алгоритм. Как мы начинаем процесс?

Начнем с выбора двух границ интервала, где мы хотим искать. Мы хотим искать весь список, поэтому мы выбираем индекс 0 как нижняя граница и индекс 5 как верхняя граница:

Средний элемент

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

В этом случае (0 + 5)//2 это 2 потому что результат 5/2 это 2.5 И целочисленное разделение усекает десятичную часть.

Таким образом, средний элемент расположен в Индекс 2 и средний элемент – это число 6 :

Сравнение

Теперь нам нужно начать сравнивать средний элемент с нашим целевым элементом, чтобы увидеть, что нам нужно сделать дальше.

Просим: Средний элемент равен элементу, который мы ищем?

6 == 67 # False

Нет, это не так.

Итак, мы спрашиваем: Это средний элемент больше, чем элемент, который мы ищем?

6 > 67 # False

Нет, это не так.

Итак, Средний элемент меньше элемента, который мы ищем.

6 < 67 # True

Откажитесь от элементов

Поскольку список уже отсортирован, это говорит нам что-то чрезвычайно важное. Он говорит нам, что мы можем «отменить» нижнюю половину списка, потому что мы знаем, что все элементы, которые приходят до среднего элемента, будут меньше элемента, который мы ищем, поэтому наш целевой элемент там нет.

Начните снова – выберите границы

Что мы делаем дальше? Мы отбросили элементы, и цикл повторяется снова.

Мы должны выбрать границы для нового интервала (см. Ниже). Но обратите внимание, что верхняя граница сохраняется неповрежденная, и изменяется только нижняя граница.

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

💡 Совет: Если средний элемент был больше элемента, который мы ищем, верхняя граница была бы изменена, и нижняя граница была бы не повреждена. Таким образом, мы бы отказались от верхней половины списка и продолжайте поиск в нижней половине.

Средний элемент

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

Результат (3 + 5)//2 это 4 Таким образом, средний элемент расположен в индекс 4 и средний элемент – 67 Отказ

Сравнение

Просим: Средний элемент равен элементу, который мы ищем?

67 == 67 # True

Да, это! Итак, мы нашли элемент при индексе 4 Отказ Возвращается значение 4, и алгоритм был успешно завершен.

💡 Совет: Если элемент не был найден, процесс продолжил бы продолжил до тех пор, пока интервал больше не был действительным. Если элемент не был найден во всем списке, – был бы возвращен.

🔹 Код прохождения

Теперь, когда у вас есть визуальная интуиция того, как алгоритм работает за кулисами, давайте погрузимся в реализацию итеративных Python, анализируя его линию по линии:

def binary_search(data, elem):

    low = 0
    high = len(data) - 1

    while low <= high:
      
        middle = (low + high)//2
       
        if data[middle] == elem:
            return middle
        elif data[middle] > elem:
            high = middle - 1
        else:
            low = middle + 1

    return -1

Заголовок

Здесь у нас есть заголовок функции:

def binary_search(data, elem):

Требуется два аргумента:

  • Упорядоченная последовательность элементов (например, список, кортеж или строка).
  • Элемент, который мы хотим найти.

Начальный интервал

Следующая строка устанавливает начальные нижние и верхние границы:

low = 0
high = len(data) - 1

Начальная нижняя граница – индекс 0 И начальная верхняя граница является последним показателем последовательности.

Петля

Мы повторим процесс, пока существует допустимый интервал, а нижняя граница меньше или равно верхней границе.

while low <= high:

💡 Совет: Помните, что границы являются индексами.

Средний элемент

На каждой итерации нам нужно найти индекс среднего элемента. Для этого мы добавляем нижние и верхние границы и разделите результат на 2 с использованием целочисленного деления.

middle = (low + high)//2

💡 Совет: Мы используем целочисленное отделение в случае, если список или интервал содержит даже количество элементов. Например, если у списка было 6 элементов, и мы не использовали целочисленное разделение, Средний будет результатом (0 + 5)/2 который является 2.5 Отказ Индекс не может быть поплавка, поэтому мы обрезаем десятичную часть, используя // и выберите элемент по индексу 2 Отказ

Сравнение

С этими условными условиями (см. Ниже), мы определяем, что делать в зависимости от значения среднего элемента Данные [середина] Отказ Мы сравниваем его с целевым элементом, который мы ищем.

if data[middle] == elem:
    return middle
elif data[middle] > elem:
    high = middle - 1
else:
    low = middle + 1

Есть три варианта:

  • Если средний элемент равен элементу, который мы ищем, мы немедленно возвращаем индекс, потому что мы нашли элемент.
if data[middle] == elem:
    return middle
  • Если средний элемент больше, чем элемент, который мы ищем, мы переназнаем верхнюю границу, потому что мы знаем, что целевой элемент находится в нижней половине списка.
elif data[middle] > elem:
    high = middle - 1
  • Кроме того, оставшийся единственный вариант – это то, что средний элемент меньше элемента, который мы ищем, поэтому мы переназнаем нижнюю границу, потому что мы знаем, что целевой элемент находится в верхней половине списка.
else:
    low = middle + 1

Элемент не найден

Если цикл завершен без нахождения элемента, возвращается значение -1.

return -1

И у нас есть окончательная реализация двоичного алгоритма поиска:

def binary_search(data, elem):

    low = 0
    high = len(data) - 1

    while low <= high:
      
        middle = (low + high)//2
       
        if data[middle] == elem:
            return middle
        elif data[middle] > elem:
            high = middle - 1
        else:
            low = middle + 1

    return -1

🔸 особые случаи

Это некоторые конкретные случаи, которые вы можете найти, как вы начинаете работать с этим алгоритмом:

Повторные элементы

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

>>> >>> b = [2, 2, 3, 6, 7, 7]
>>> binary_search(b, 7)
4

Элемент не найден

Если элемент не найден, -1 возвращается.

>>> b = [2, 2, 3, 6, 7, 7]
>>> binary_search(b, 8)
-1

Пустая последовательность

Если последовательность пуста, -1 будет возвращена.

>>> b = []
>>> binary_search(b, 8)
-1

Недоставленная последовательность

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

Этот пример возвращает правильный результат:

>>> b = [5, 7, 3, 0, -9, 2, 6]
>>> binary_search(b, 6)
6

Но этот не:

>>> b = [5, 7, 3, 0, -9, 2, 10, 6]
>>> binary_search(b, 6)
-1

💡 Совет: Подумайте, почему первый пример возвращает правильный результат. Подсказка: это чистое совпадение о том, что порядок элементов происходит, чтобы воплотить алгоритм достичь правильного индекса, но пошаговый процесс оценивает 0 Тогда 2 и, наконец, 6 Отказ В этом конкретном случае для этого конкретного элемента правильный индекс найден, даже если последовательность не отсортирована.

🔹 более сложный пример

Теперь, когда вы более знакомы с алгоритмом и его реализацией Python, здесь у нас есть более сложный пример:

Мы хотим найти индекс элемента 45 В этом списке с помощью двоичного поиска:

Первая итерация

Нижние и верхние границы выбираются:

Выбран средний элемент ( 26 ):

Но средний элемент ( 26 ) не является элементом, который мы ищем, он меньше, чем 45 :

Вторая итерация

Таким образом, мы можем отменить все элементы, которые меньше среднего элемента и выберите новые границы. Новая нижняя граница ( 27 ) – это элемент, расположенный сразу справа от предыдущего среднего элемента:

💡 Совет: Помните, что список уже отсортирован.

Выбран новый средний элемент ( 30 ):

Средний элемент ( 30 ) не является элементом, который мы ищем, он меньше, чем 45 :

Третья итерация

Мы можем отменить элементы, меньшие или равные 30 которые уже не отбрасываются. Нижняя граница обновляется до 32 :

Здесь у нас есть интересный случай: средний элемент является одним из границ текущего интервала, потому что (7 + 8)//2 это 7 Отказ

Средний элемент ( 32 ) не является элементом, который мы ищем ( 45 ), он меньше.

Четвертая итерация

Мы можем отменить элементы, меньшие или равные 32 которые уже не отбрасываются.

Здесь у нас есть еще один очень интересный случай: интервал имеет только один элемент.

💡 Совет: Этот интервал действителен, потому что мы написали это состояние Хотя высокое: , который включает в себя интервалы, где индекс нижней границы равен индексу верхней границы.

Средний элемент – единственный элемент в интервале, потому что (8 + 8)//2 это 8 поэтому индекс среднего элемента – 8 и средний элемент – 45 Отказ

Теперь средний элемент – это элемент, который мы ищем, 45 :

Так что ценность 8 (индекс) возвращается:

>>> binary_search([1, 3, 7, 15, 26, 27, 30, 32, 45], 45)
8

🔸 дополнительная практика

Если вы хотите получить дополнительную практику с этим алгоритмом, попробуйте объяснить, как алгоритм работает за кулисами, когда он применяется к этому списку, чтобы найти целое число 90 :

[5, 8, 15, 26, 38, 56]
  • Что бывает шаг за шагом?
  • Какое значение возвращается?
  • Это элемент найден?

Я действительно надеюсь, тебе понравилась моя статья и обнаружила, что это полезно. Теперь вы можете реализовать алгоритм двоичного поиска в Python. Проверьте мой онлайн курс « Алгоритмы поиска и сортировки Python: практический подход ». Следуй за мной на Twitter Отказ ⭐️.