Сколько сравнений будет бинарный поиск в худшем случае с использованием этого алгоритма?
Привет, ниже приведен псевдокод для реализации бинарного поиска:
Input: (A[0...n-1], K)
begin
l ← 0; r ← n-1
while l ≤ r do
m ← floor((l+r)/2)
if K > A[m] then l ← m+1
else if K < A[m] then r ← m-1 else return m
end if
end while
return -1 // key not found
end
Мне просто интересно, как вычислить количество сравнений, которые эта реализация сделала бы в худшем случае для отсортированного массива размера n?
Будет ли количество сравнений = lg n + 1? или что-то другое?
Ответы
Ответ 1
В худшем случае в этом случае, если элемент K отсутствует в и меньше всех элементов в A. Тогда мы имеем два сравнения на каждом шаге: K > A[m]
и K < A[m]
.
Для каждого шага массив разбивается на две части, каждая из которых имеет размер (n-1)/2
, мы имеем максимум шагов log_2(n-1)
.
Это приводит к сумме сравнений 2*log_2(n-1)
, которые асимптотически действительно равны O(log(n))
.
Ответ 2
Очень незначительная коррекция hielsnoppe answer:
В массиве n
-element (n > 0
) сравниваемый элемент имеет индекс m = floor((n-1)/2)
. Таким образом, есть три возможности
-
A[m] < K
, то после одного сравнения поиск продолжается в массиве n-1-m = ceiling((n-1)/2)
-element.
-
A[m] > K
, то после двух сравнений поиск продолжается в массиве m
-element.
-
A[m] == K
, тогда мы закончим после двух сравнений.
Итак, если мы обозначим максимальное (наихудшее) количество сравнений для поиска в массиве n
-element на C(n)
, мы имеем
C(0) = 0
C(n) = max { 1 + C(ceiling((n-1)/2), 2 + C(floor((n-1)/2) }, n > 0
Для нечетного n = 2k+1
пол и потолок идентичны, поэтому максимум, по-видимому, является последним,
C(2k+1) = 2 + C(k)
и для четного n = 2k
находим
C(2k) = max { 1 + C(k), 2 + C(k-1) }.
Для n = 2
, который разрешает C(2) = 1 + C(1) = 1 + 2 = 3
, для всех больших четных n
максимальный 2 + C(k-1)
, так как для n >= 1
имеем C(n) <= C(n+1) <= C(n) + 1
.
Оценивая рекурсию для первых нескольких n
, находим
C(0) = 0
C(1) = 2
C(2) = 3
C(3) = C(4) = 4
C(5) = C(6) = 5
C(7) = C(8) = C(9) = C(10) = 6
C(11) = ... = C(14) = 7
C(15) = ... = C(22) = 8
C(23) = ... = C(30) = 9
Итак, по индукции докажем
C(n) = 2k, if 2^k <= n+1 < 2k + 2^(k-1), and
C(n) = 2k+1, if 2^k + 2^(k-1) <= n+1 < 2^(k+1)
или
C(n) = 2*log2(n+1) + floor(2*(n+1)/(3*2^floor(log2(n+1)))).
Это точная верхняя граница.
Ответ 3
Согласно странице wikipedia на бинарный поиск, наихудшая производительность этого алгоритма O(lg n)
, которая измеряет асимптотическое число необходимы сравнения. Фактическое наихудшее количество сравнений было бы 2*lg(n-1)
, как указано в ответе @hielsnoppe.
Псевдокод в вопросе представляет собой типичную реализацию двоичного поиска, поэтому ожидаемые сложности производительности сохраняются для массива (или вектора) размера n
:
- Лучшая производительность:
O(1)
- Средняя производительность:
O(lg n)
- Наихудшая производительность:
O(lg n)
При ближайшем рассмотрении в вопросе псевдокода есть две проблемы:
- Строка:
if K > A[m] then return l ← m+1
должна читать if K > A[m] then l ← m+1
. Вы еще не можете вернуться
- Строка:
m ← floor((l+r)/2)
может привести к переполнению, если числа достаточно велики при работе с целыми числами фиксированного размера. Правильный синтаксис варьируется в зависимости от используемого языка программирования, но что-то по этому поводу устранит проблему: m ← (l + r) >>> 1
, где >>>
- это беззнаковый оператор сдвига вправо. Подробнее о проблеме в здесь.