В следующем фрагменте Python находится реализация бинарного поиска; найдите все логические и граничные ошибки, исправьте код, докажите корректность исправленного алгоритма и оцените его временную сложность: def binary_search(a, x): l = 0 r = len(a) while l <= r: m = (l+r)//2 if a[m] == x: return m if a[m]

2 Окт в 17:16
12 +1
0
Ответы
1

Найденный фрагмент содержит реализацию бинарного поиска с ошибками. Сначала перечислю ошибки, потом приведу исправленный код, далее дам формальное обоснование корректности (инвариант + завершение) и оценю временную сложность.

1) Ошибки и пограничные случаи

Неверная инициализация правой границы: r = len(a). Правильна r = len(a) - 1. При r = len(a) возможен выход индекса m == len(a) и IndexError (особенно заметно при пустом массиве или когда l становится len(a)).При пустом списке (len(a) == 0) исходный код сразу обращается к a[0] и падает.Код не указывает как вести себя при неотсортированном массиве — это не ошибка реализации, а требование алгоритма: бинарный поиск корректен только для отсортированного массива (по возрастанию в данном варианте).(Опционально) Если требуется найти первую/последнюю позицию при наличе множества равных элементов, алгоритм нужно слегка модифицировать — текущая версия вернёт некоторую позицию, где найден x.

2) Исправленный код (итеративный)
def binary_search(a, x):
l = 0
r = len(a) - 1
while l <= r:
m = (l + r) // 2
if a[m] == x:
return m
elif a[m] < x:
l = m + 1
else:
r = m - 1
return -1

(предположение: a — список, отсортированный по возрастанию)

3) Доказательство корректности
Определим n = len(a). Инвариант цикла:
На начале каждой итерации while поддерживается: если x ∈ a, то x ∈ a[l : r+1] (то есть возможная позиция x находится в текущем замкнутом интервале индексов [l, r]).

Инициализация. До первой итерации l = 0, r = n-1, то есть интервал покрывает весь массив. Если x есть в массиве, его индекс ∈ [0, n-1], инвариант верен.

Сохранение. В теле цикла вычисляем m = (l + r) // 2.

Если a[m] == x — алгоритм возвращает m (корректный ответ).Если a[m] < x — так как массив отсортирован по возрастанию, все элементы с индексом ≤ m меньше x, следовательно возможные позиции x находятся в индексах > m. Мы устанавливаем l = m + 1; новый интервал [l, r] = [m+1, r] содержит все возможные позиции x, инвариант сохраняется.Если a[m] > x — аналогично устанавливаем r = m - 1, новый интервал [l, r] = [l, m-1] содержит все возможные позиции x.
Таким образом инвариант поддерживается.

Завершение. Цикл завершается тогда и только тогда, когда l > r. По инварианту: если x был в массиве, то перед завершением его индекс ∈ [l, r]. Но при l > r интервал пуст, следовательно x не принадлежит массиву. Тогда возвращается -1. Если x найден — возвращается индекс при a[m] == x. Следовательно алгоритм корректно определяет наличие и позицию x (в случае нескольких вхождений — одно из них).

Проверка отсутствия бесконечного цикла: на каждой итерации либо возвращаем ответ, либо сдвигаем l вправо на ≥1 (l = m+1) либо сдвигаем r влево на ≥1 (r = m-1). Размер интервала r-l+1 уменьшается как минимум на 1 в каждой итерации, значит цикл завершится за конечное число шагов.

4) Временная сложность
На каждой итерации длина рассматриваемого интервала примерно делится на 2 (интервал уменьшается как минимум вдвое в среднем), поэтому число итераций O(log n). Точное число итераций в худшем случае ≤ floor(log2(n)) + 1. Каждая итерация делает O(1) операций, следовательно общая временная сложность O(log n). Память — O(1) дополнительно.

5) Дополнения

Если нужен первый (левый) индекс при наличии дубликатов, можно при a[m] == x запоминать m и продолжать поиск в левой части (r = m-1), затем вернуть последний сохранённый m; для последнего (правого) индекса аналогично.Если массив отсортирован по убыванию, сравнения нужно инвертировать (если a[m] < x, то r = m-1 и т.п.).
2 Окт в 18:08
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир