Алгоритмы (11 частью серии)
Бинарный поиск – это воскорь-быстрый алгоритм поиска, который имеет худший случай Большой o Ο (log n)
средний случай – O (log n)
И лучший случай – O (1)
. Короче говоря, это алгоритм поиска, который так же быстро, как вы можете получить.
Двоичный поиск использует деление и поколение, чтобы уменьшить большую часть потенциального времени выполнения. На самом деле, это очень похоже на слияние в том, как он работает Но, конечно, один алгоритм используется для сортировки данных, и этот используется для поиска данных.
Для правильной работы алгоритма данные должны быть предварительно отсортированы и иметь элементы того же или аналогичных типов. Поэтому, если ваша коллекция содержит строки и целые числа, он не будет работать, у вас должны быть элементы с использованием последовательного типа данных Inten в вашей коллекции.
Когда двоичный поиск выполнен, он возьмет ввод Коллекция
и а ценность
найти, и если значение существует, мы вернем индекс этого ценность
в пределах Коллекция
И если это не так, мы вернемся -1
вне конвенции, чтобы представлять, что ценность
не существует в коллекция
после всего.
Тесты
from main import binarySearch; def test_strings(): assert binarySearch(["Dave", "James"], "Dave") == 0; assert binarySearch(["Dave", "James"], "abc") == -1; def test_integers(): assert binarySearch([1, 2, 3,4], 3) == 2; assert binarySearch([1, 2, 3, 4], 5) == -1; def test_floats(): assert binarySearch([3.14], 3.14) == 0; assert binarySearch([3.141], 3.14) == -1; def test_booleans(): assert binarySearch([False, True], True) == 1; assert binarySearch([True], False) == -1; def test_lists(): assert binarySearch([[3.14]], [3.14]) == 0; assert binarySearch([[3.141]], [3.14]) == -1; def test_nested_lists(): assert binarySearch([[3.14, [1]]], [3.14, [1]]) == 0; assert binarySearch([[3.141]], [3.14]) == -1; def test_sets(): assert binarySearch([set([1])], set([1])) == 0; assert binarySearch([set([1])], set([1,2])) == -1; def test_tuples(): assert binarySearch([tuple([0,2]), tuple([1])], tuple([1])) == 1; assert binarySearch([tuple([1])], tuple([1, 2])) == -1;
В Python, как и со многими другими языками, некоторые элементы не могут сравниться друг с другом без взлома, даже если они являются одними тем же типом. Примером этого являются такими типами, как Дикт
не может запустить сравнения, такие как {'A': 1}> {'A': 1}
Отказ
Для двоичного поиска нам нужно использовать Сравнение операторов Таким образом, хотя И поэтому я представлял только типы, которые совместимы с ==.
, >
, >=
, <
и <=
Операторы, которые бинарный поиск используют.
Вот заметка, чтобы запустить и играть с тестами:
Реализация
Есть несколько способов кожи этой конкретной кошки. По этой причине я покажу вам две реализации для двоичного алгоритма поиска. Один будет использовать Императив подход и другой, используя Рекурсия .
Оба реализации ожидают единые данные и набираются по этой причине. Мы ожидаем, что данные будут содержать постоянные типы в коллекции ввода, чтобы поиск гарантированно сможет сравнить элементы в коллекции против друг друга. Если мы этого не сделали, мы можем получить неожиданные ошибки, такие как это:
TypeError: ‘>’ не поддерживается между экземплярами «Список» и «STR»
Просто не забудьте нормализовать и сортировать свои данные, прежде чем начать использовать реализации, которые будут изложены ниже, или если вы решите создать собственную реализацию в будущем, поскольку эти черты являются требованием алгоритма.
Императивный подход
Императивное программирование – это парадигма программирования, которая описывает Как Программа должна работать. Эта парадигма использует заявления Чтобы изменить состояние программ и генерировать значения.
from typing import List, TypeVar; from operator import eq as deep_equals, gt as greater_than, ne as not_equal, le as less_than_or_equal; from math import floor; T = TypeVar('T'); def binarySearch(collection: List[T], value: T) -> int: start = 0; end = len(collection) - 1; middle = floor((start + end) / 2); while(not_equal(collection[middle], value) and less_than_or_equal(start, end)): if greater_than(value, collection[middle]): start = middle + 1; else: end = middle - 1; middle = floor((start + end) / 2); return middle if deep_equals(collection[middle], value) else -1;
Мы начинаем, импортируя некоторые помощники из Python Standard Библиотека Кто имеет целый снатму помощника для нас, чтобы использовать в нашем коде.
Стоит отметить, это использование Typevar
от Набрав
Модуль для выравнивания наших типов ввода для Коллекция
и стоимость
.
Все это значит, что когда мы получим коллекцию, у него должны быть предметы постоянного типа T
и стоимость
Мы хотим искать, должен также быть типа T
.
Так что, если у нас есть Коллекция
типа Список [ул.]
Например, тогда значение также должно быть ул
Но вместо того, чтобы труднокодировать это, мы позволяем Typevar.
Сделайте для нас работу.
Наше Бинарный поиск
Функция получит Коллекция
Для поиска внутри и A ценность
искать в этом Коллекция
Отказ Если ценность
Найден, тогда мы возвращаем его местоположение (индекс) в Коллекция
Отказ
Чтобы найти индекс, который мы настроим Начало
, конец
и Средний
Индексы Коллекция
Отказ Пока ценность
не равен ценности в Средний
из Коллекция
и Начать
Индекс меньше или равен конец
Индекс, мы проверяем, если ценность
больше, чем Средний
Значение, если это так, мы устанавливаем Начать
Индекс, чтобы начать на следующей итерации от середина
, в противном случае мы ставим конец Средний
Отказ
Мы делаем это, потому что если ценность
больше, чем значение в Средний
из Коллекция
, ценность
Должен быть на правой половине коллекции и наоборот на обратный случай.
И любой способ падает условие, мы сбросим Средний
индекс, чтобы быть между новым Начало
и конец
индексы. Мы продолжаем с этим процессом до тех пор, пока контур не является верным.
Когда цикл While выходит, мы проверяем, если Средний
Значение индексов – это стоимость
мы ищем. Если это так, мы вернем Средний
индекс и если это не так ценность
не было в коллекции ввода, и мы возвращаемся -1
представлять это дело.
Рекурсивный подход
from typing import List, TypeVar, Union; from operator import eq as deep_equals, gt as greater_than, ne as not_equal, le as less_than_or_equal, ge as greater_than_or_equal; from math import floor; T = TypeVar('T'); def binarySearch(collection: List[T], value: T, start: int = 0, end: Union[int, None] = None) -> int: if deep_equals(end, None): end = len(collection) - 1; if greater_than_or_equal(end, start): middle = floor((start + end) / 2); if deep_equals(collection[middle], value): return middle; if greater_than(collection[middle], value): return binarySearch(collection, value, start, middle - 1); return binarySearch(collection, value, middle + 1, end); return -1;
Точно так же, как императивный подход, изложенный выше, мы начинаем, импортируя некоторые помощники из Python Standard Библиотека Кто имеет целый снатму помощника для нас, чтобы использовать в нашем коде.
Стоит отметить, это использование Typevar
от Набрав
Модуль для выравнивания наших типов ввода для Коллекция
и стоимость
.
Все это значит, что когда мы получим коллекцию, у него должны быть предметы постоянного типа T
и стоимость
Мы хотим искать, должен также быть типа T
.
Так что, если у нас есть Коллекция
типа Список [ул.]
Например, тогда значение также должно быть ул
Но вместо того, чтобы труднокодировать это, мы позволяем Typevar.
Сделайте для нас работу.
Рекурсивная версия естественным образом называет себя снова и снова и требует дополнительных параметров, которые необходимо предоставить на каждом вызове, чем требуется императивный подход. А именно нам нужно знать, где Начать
индекс и конец
Индекс для текущего рекурсивного вызова Бинарный поиск
функция.
Первоначально Начать
будет в 0
что имеет смысл, поскольку мы хотим начать в начале Коллекция
и конец
Первоначально будет установлен на Нет
Но будет установлен на длину Коллекция
Mins один, как только функция первой запускается.
Примечание: мы устанавливаем конец
инициализировать себя со значением Нет
Поскольку в Python вы не можете ссылаться на другие параметры для использования в качестве значений по умолчанию других параметров. Чтобы обойти это, мы устанавливаем его тип для Союз [Нет, int]
который просто означает, что это может быть типа Нет
или int.
. Как только функция сначала выполняет, мы устанавливаем конец
Значение длины Коллекция
мин один, потому что если Коллекция
Содержит 5 элементов Последний индекс будет 4, поскольку списки/массивы всегда 0 индексированы.
Если конец
Индекс больше или равен начальным индексе, который мы рассчитаем, где Средний
Индекс текущего Коллекция
Есть и рассмотрим 3 случая:
- Если товар на
Средний
Индекс равен ценности, который мы ищем, вернитеСредний
показатель - Если товар на
Средний
Индекс больше, чем значение, которое мы ищем, то запустите рекурсивный звонок дляБинарный поиск
Чтобы проверить левую половинуКоллекция
длястоимость
- Если товар на
Средний
Индекс меньше, чем значение, которое мы ищем, то запустите рекурсивный звонок наБинарный поиск
Чтобы проверить правильную половинуКоллекция
длястоимость
Если ни один из этих рекурсивных вызовов не удается найти ценность
в коллекция
Тогда мы возвращаем -1
по умолчанию.
Выводы
Двоичный алгоритм поиска всегда является фантастическим выбором для поиска значений в сортировке и последовательных наборах данных.
Лично я стараюсь всегда держать данные в приложениях как можно максимально равно, и, таким образом, я нахожусь, используя этот алгоритм относительно часто в моем повседневной работе. Я настоятельно рекомендую посмотреть в него дальше и увидеть, как это может принести вам пользу и вашу работу!
Я надеюсь, что вы узнали что-то новое сегодня, и если у вас есть какие-либо вопросы, идеи или комментарии, не стесняйтесь комментировать их ниже!
Алгоритмы (11 частью серии)
Оригинал: “https://dev.to/jamesrweb/binary-search-1m4g”