Алгоритмы (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”