Можно ли проводить только одно сравнение на итерацию алгоритма двоичного поиска?

В алгоритме двоичного поиска у нас есть два сравнения:

if (key == a[mid]) then found;

else if (key < a[mid]) then binary_search(a[],left,mid-1);
      else binary_search(a[],mid+1,right);

Есть ли способ, с помощью которого я могу иметь только одно сравнение вместо двух вышеупомянутых.

-

Спасибо

Alok.Kr.

6
задан Lazer 17 August 2010 в 18:36
поделиться

7 ответов

См .:

http://en.wikipedia.org/wiki/Binary_search_algorithm#Single_comparison_per_iteration

Взято из вики:

   low = 0
   high = N
   while (low < high) {
       mid = low + ((high - low) / 2)
       if (A[mid] < value)
           low = mid + 1;
       else
            //can't be high = mid-1: here A[mid] >= value,
            //so high can't be < mid if A[mid] == value
            high = mid;
   }
   // high == low, using high or low depends on taste
   if ((low < N) && (A[low] == value))
       return low // found
   else
       return -1 // not found

Плюсы / минусы из вики: «Этот подход исключает возможность досрочного завершения при обнаружении совпадения, поэтому успешные поиски имеют log2 (N) итераций вместо ожидаемых log2 (N) - 1 итераций. С другой стороны, эта реализация делает меньше сравнений: log2 (N ) меньше ожидаемого числа сравнений для двухтестовых реализаций 1 · 5 (log2 (N) - 1) для N больше восьми ».

14
ответ дан 8 December 2019 в 12:17
поделиться

Да. Только не удаляйте mid из рекурсивного вызова.

if ( left == right ) return NULL;
if ( left + 1 == right ) return key == a[left]? &a[left] : NULL;

mid = left + ( right - left / 2 );

if (key < a[mid]) return binary_search(a[],left,mid-1);
else return binary_search(a[],mid,right); // include `mid` in next round

Чтобы добиться производительности O (logN), вам нужно исключать только половину набора при каждой рекурсии. Вы делаете все возможное, удаляя половину + 1.

Если вы используете только < во время рекурсии, алгоритм найдет наименьший элемент, который не меньше, чем ключ (но может быть больше, чем ключ ). . Закончите, выполнив единственный тест на равенство.

4
ответ дан 8 December 2019 в 12:17
поделиться

На ассемблере вы можете:

cmp key,a[mid]
beq found
bge else

Так что, если ваш компилятор действительно хорош в оптимизации с помощью глазок, он, возможно, уже сделает это за вас.

1
ответ дан 8 December 2019 в 12:17
поделиться

Это рекурсивный алгоритм. Первое сравнение - это критерии остановки, а второе - фактический поиск, поэтому вы не можете их удалить.

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

0
ответ дан 8 December 2019 в 12:17
поделиться

Перво-наперво: нужно ли вам оптимизировать программу? Вы измерили, чтобы знать, где это нужно делать? Это в этой функции?

Для примитивных типов второе сравнение является максимально быстрой операцией. Более высокая стоимость сравнения - загрузка элемента в соответствующий регистр, и это необходимо для первого сравнения. Как только это сравнение выполнено, значение уже находится в регистре, и вторая операция требует одной инструкции процессора плюс возможные затраты на неверное предсказание перехода.

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

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

Если вы говорите о C ++, а тип сложен, а перегруженные операторы сравнения дороги, самым быстрым приростом производительности является реализация метода compare , который вернет отрицательное число за меньше -тем , 0 для равного и положительное число, если больше, -чем . Затем предварительно вычислите результат перед сравнениями, а затем выполните только целочисленные проверки.Это снизит общую стоимость алгоритма до единственной обработки реальных объектов с дорогостоящим сравнением и вернет вас к исходному предположению.

0
ответ дан 8 December 2019 в 12:17
поделиться
for (step = 0; step < n; step <<= 1);

for (i = 0; step; step >>= 1)
    if (i + step < n && v[i + step] <= x)
        i += step;
0
ответ дан 8 December 2019 в 12:17
поделиться

Хорошо, это был вопрос интервью в Adobe, и я просто пытался понять, как это сделать.

Теперь у меня есть решение, поэтому я публикую

void binary_search (int a[] , int low ,  int high , int key )
{
    int mid = (low+high)/2;

    if (key == a[mid]) {
        printf ("Number Found\n");
        return;
    }

    else {
        int sign = Calc_sign (key-a[mid]);
        low = low*sign + (1-sign)*mid;
        high = mid*sign + (1-sign)*right;
        binary_search (a,low,high,key);
    }
}

int Calc_sign(int a) 
{
     return ( (a & 80000000) >> 31);
}

Итак, в коде будет только одно сравнение для проверки того, соответствует ли ключевое значение среднему элементу.

-

Спасибо

Алок Кр.

0
ответ дан 8 December 2019 в 12:17
поделиться
Другие вопросы по тегам:

Похожие вопросы: