2016-07-25 1 views
3

Я изучаю итераторы и застрял в течение 3-х дней на выяснение того, почему мы используем:Двоичный поиск с использованием итераторов, почему мы используем «(end-begin)/2»?

auto mid = text.begin() + (end - beg)/2; 

Код:

int main() 

{ 
    vector<int> text{ 10,9,8,7,6,5,4,3,2,1 }; 
    int sought = 3; 
    // text must be sorted 
    // beg and end will denote the range we're searching 
    auto beg = text.begin(), end = text.end(); 
    auto mid = text.begin() + (end - beg)/2; // original midpoint 
               // while there are still elements to look at and we haven't yet found sought 
    while (mid != end && *mid != sought) { 
     if (sought < *mid) // is the element we want in the first half? 
      end = mid; // if so, adjust the range to ignore the second half 
     else // the element we want is in the second half 
      beg = mid + 1; // start looking with the element just after mid 
     mid = beg + (end - beg)/2;// new midpoint 
    } 

    system("pause"); 
} 

почему

auto mid = text.begin() + (end - beg)/2; 

и нет:

auto mid = text.begin() + text.size()/2; 

Пожалуйста, помогите.

+0

ли *** мы *** использование "(конец - начало)/2"? Где ты нашел это? – Wolf

+1

@ Wolf - C++ primer 5th edition. Его немного вводит в заблуждение, поскольку книга в главе 3.4 говорит, что это «классический алгоритм», поэтому я предположил, что это было обычное явление (исправьте меня, если я ошибаюсь) – jibzoiderz

+1

Причина, по которой это путает, заключается в том, что в примере реализуется двоичный поиск внутри основная функция. Если он был правильно извлечен в функцию, для которой требуется только диапазон итераторов для поиска, было бы понятно, почему вы не можете назвать размер в контейнере, потому что у вас нет способа ссылаться на контейнер. –

ответ

3

Бинарный поиск традиционно написан так. Эта форма написания помогает кодировщикам понимать двоичный поиск, поскольку в стандартном бинарном поиске используется только начало, конец, середина.

Вы могли использовать size() вместо end-starперед циклом, но вы должны использовать end-start в то время как петли, так как end-start изменится. Вы должны избегать использования size() для обеспечения согласованности.

+0

, так что это больше формальности? – jibzoiderz

+0

@jibzoiderz Да, но конец цикла во время цикла не может быть изменен. –

+0

Кстати, я предпочитаю (beg + end)/2, чем beg + (beg-end)/2 лично, он является формальным и сохраняет код. –

4

Это делается для того, чтобы избежать переполнения, которое может произойти при добавлении двух очень больших целых чисел, где результат сложения может превышать максимальный предел максимального значения и дает странные результаты.

Extra, Extra - Read All About It: Nearly All Binary Searches and Mergesorts are Broken

Из блога:

So what's the best way to fix the bug? Here's one way: 
6:    int mid = low + ((high - low)/2); 

Probably faster, and arguably as clear is: 
6:    int mid = (low + high) >>> 1; 

In C and C++ (where you don't have the >>> operator), you can do this: 
6:    mid = ((unsigned int)low + (unsigned int)high)) >> 1; 
+0

Указатели не поддерживают добавление вообще; большие целые индексы редко переполняются (размер массива обычно намного меньше предела целых чисел). –

+0

Ну, я говорю об общей технике и конкретно не привязаны к указателям. Вопрос предполагает, что ОП только спрашивает, почему нет (beg + end)/2? Пожалуйста, прочитайте ссылку (Блог исследований Google) в своем ответе, чтобы понять более подробно. – Yavar