Сколько сравнений будет выполнять двоичный поиск в худшем случае с использованием этого алгоритма?
Привет ниже приведен псевдо-код для моей реализации двоичного поиска:
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? или что-то другое?
3 ответов
в худшем случае в этом случае, если элемент K отсутствует в A и меньше всех элементов в A. тогда у нас есть два сравнения на каждом шаге:K > A[m]
и K < A[m]
.
ибо на каждом шаге массив разрезается на две части, каждая размером (n-1)/2
, у нас есть максимум log_2(n-1)
действия.
это приводит к в общей сложности 2*log_2(n-1)
сравнения, которые асимптотически действительно равны O(log(n))
.
очень незначительная поправка к ответ hielsnoppe:
на n
-элемент массива (n > 0
), элемент для сравнения находится в index m = floor((n-1)/2)
. Итак, есть три возможности
-
A[m] < K
, затем после одного сравнения поиск продолжается вn-1-m = ceiling((n-1)/2)
-элемент массива. -
A[m] > K
, затем после двух сравнений, поиск продолжается вm
-элемент массива. -
A[m] == K
, тогда мы закончили после два сравнения.
Итак, если мы обозначим максимальное (в худшем случае) число сравнений при поиске в n
-элемент массива 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)))).
это точная верхняя граница.
согласно странице Википедии о бинарный поиск, наихудшая производительность этого алгоритма -O(lg n)
, который измеряет асимптотическое число необходимых сравнений. The фактический в худшем случае количество сравнений будет 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
. Ты еще не можешь вернуться!--21--> - строку:
m ← floor((l+r)/2)
может вызвать переполнение, если числа достаточно большие, когда работа с целыми числами фиксированного размера. Правильный синтаксис варьируется в зависимости от фактического языка программирования, который вы используете, но что-то в этом исправит проблему:m ← (l + r) >>> 1
, где>>>
- оператор сдвига вправо без знака. Подробнее о проблеме читайте в здесь.