Какой самый быстрый метод поиска для отсортированного массива? - PullRequest
23 голосов
/ 21 января 2011

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

Однако я уверен, что есть способы оптимизировать эти функции, чтобы сделать их еще быстрее.У кого-нибудь есть идеи, как можно сделать эту функцию поиска быстрее?Решение на C или C ++ является приемлемым, но оно мне нужно для обработки массива с 100000 элементов.

#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <stdint.h>
#include <assert.h>

static __inline__ unsigned long long rdtsc(void)
{
  unsigned long long int x;
     __asm__ volatile (".byte 0x0f, 0x31" : "=A" (x));
     return x;
}

int interpolationSearch(int sortedArray[], int toFind, int len) {
    // Returns index of toFind in sortedArray, or -1 if not found
    int64_t low = 0;
    int64_t high = len - 1;
    int64_t mid;

    int l = sortedArray[low];
    int h = sortedArray[high];

    while (l <= toFind && h >= toFind) {
        mid = low + (int64_t)((int64_t)(high - low)*(int64_t)(toFind - l))/((int64_t)(h-l));

        int m = sortedArray[mid];

        if (m < toFind) {
            l = sortedArray[low = mid + 1];
        } else if (m > toFind) {
            h = sortedArray[high = mid - 1];
        } else {
            return mid;
        }
    }

    if (sortedArray[low] == toFind)
        return low;
    else
        return -1; // Not found
}

int interpolationSearch2(int sortedArray[], int toFind, int len) {
    // Returns index of toFind in sortedArray, or -1 if not found
    int low = 0;
    int high = len - 1;
    int mid;

    int l = sortedArray[low];
    int h = sortedArray[high];

    while (l <= toFind && h >= toFind) {
        mid = low + ((float)(high - low)*(float)(toFind - l))/(1+(float)(h-l));
        int m = sortedArray[mid];

        if (m < toFind) {
            l = sortedArray[low = mid + 1];
        } else if (m > toFind) {
            h = sortedArray[high = mid - 1];
        } else {
            return mid;
        }
    }

    if (sortedArray[low] == toFind)
        return low;
    else
        return -1; // Not found
}

int binarySearch(int sortedArray[], int toFind, int len) 
{
    // Returns index of toFind in sortedArray, or -1 if not found
    int low = 0;
    int high = len - 1;
    int mid;

    int l = sortedArray[low];
    int h = sortedArray[high];

    while (l <= toFind && h >= toFind) {
        mid = (low + high)/2;

        int m = sortedArray[mid];

        if (m < toFind) {
            l = sortedArray[low = mid + 1];
        } else if (m > toFind) {
            h = sortedArray[high = mid - 1];
        } else {
            return mid;
        }
    }

    if (sortedArray[low] == toFind)
        return low;
    else
        return -1; // Not found
}

int order(const void *p1, const void *p2) { return *(int*)p1-*(int*)p2; }

int main(void) {
    int i = 0, j = 0, size = 100000, trials = 10000;
    int searched[trials];
    srand(-time(0));
    for (j=0; j<trials; j++) { searched[j] = rand()%size; }

    while (size > 10){
        int arr[size];
        for (i=0; i<size; i++) { arr[i] = rand()%size; }
        qsort(arr,size,sizeof(int),order);

        unsigned long long totalcycles_bs = 0;
        unsigned long long totalcycles_is_64 = 0;
        unsigned long long totalcycles_is_float = 0;
        unsigned long long totalcycles_new = 0;
        int res_bs, res_is_64, res_is_float, res_new;
        for (j=0; j<trials; j++) {
            unsigned long long tmp, cycles = rdtsc();
            res_bs = binarySearch(arr,searched[j],size);
            tmp = rdtsc(); totalcycles_bs += tmp - cycles; cycles = tmp;

            res_is_64 = interpolationSearch(arr,searched[j],size);
            assert(res_is_64 == res_bs || arr[res_is_64] == searched[j]); 
            tmp = rdtsc(); totalcycles_is_64 += tmp - cycles; cycles = tmp;

            res_is_float = interpolationSearch2(arr,searched[j],size);
            assert(res_is_float == res_bs || arr[res_is_float] == searched[j]); 
            tmp = rdtsc(); totalcycles_is_float += tmp - cycles; cycles = tmp;
        }
        printf("----------------- size = %10d\n", size);
        printf("binary search          = %10llu\n", totalcycles_bs);
        printf("interpolation uint64_t = %10llu\n",  totalcycles_is_64);
        printf("interpolation float    = %10llu\n",  totalcycles_is_float);
        printf("new                    = %10llu\n",  totalcycles_new);
        printf("\n");
        size >>= 1;
    }
}

Ответы [ 8 ]

15 голосов
/ 24 января 2011

Если у вас есть некоторый контроль над макетом данных в памяти, вы можете посмотреть на массивы Judy.

Или предложить более простую идею: бинарный поиск всегда сокращает пространство поискав половине.Оптимальная точка отсечения может быть найдена с помощью интерполяции (точка отсечения НЕ должна быть местом, где должен находиться ключ, а должна быть точкой, которая минимизирует статистическое ожидание пространства поиска для следующего шага).Это минимизирует количество шагов, но ... не все шаги имеют одинаковую стоимость.Иерархическая память позволяет выполнять несколько тестов одновременно с одним тестом, если локальность поддерживается.Поскольку первые M шагов бинарного поиска касаются не более 2 ** M уникальных элементов, их совместное хранение может привести к гораздо лучшему сокращению пространства поиска для каждой выборки строки кэша (не для сравнения), что является более высокой производительностью в реальном мире.

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

Итог: даже «Оперативное запоминающее устройство» (ОЗУ) быстрее при последовательном доступе, чемслучайным образом.Алгоритм поиска должен использовать этот факт в своих интересах.

9 голосов
/ 01 февраля 2011

Тест на Win32 Core2 Quad Q6600, gcc v4.3 msys. Компиляция с g ++ -O3, ничего особенного.

Наблюдение - утверждения, время и издержки цикла составляют около 40%, поэтому любые усиления, перечисленные ниже, следует разделить на 0,6, чтобы получить реальное улучшение в тестируемых алгоритмах.

Простые ответы:

  1. На моей машине замена int64_t на int для «low», «high» и «mid» в interpolationSearch дает ускорение от 20% до 40%. Это самый быстрый и простой способ, который я смог найти. На моем компьютере требуется около 150 циклов (при размере массива 100000). Это примерно то же количество циклов, что и при пропадании кэша. Так что в реальных приложениях уход за вашим кешем, вероятно, будет самым большим фактором.

  2. Замена "/ 2" функции binarySearch на ">> 1" увеличивает скорость на 4%.

  3. Использование алгоритма STL binary_search для вектора, содержащего те же данные, что и "arr", примерно равно скорости, которую выполняет двоичный поиск вручную. Хотя на меньшем «размере» STL гораздо медленнее - около 40%.

4 голосов
/ 21 января 2011

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

Есть три файла для демонстрации.

Сортировка регрессии / код поиска:

#include <sstream>
#include <math.h>
#include <ctime>
#include "limits.h"

void insertionSort(int array[], int length) {
   int key, j;
   for(int i = 1; i < length; i++) {
      key = array[i];
      j = i - 1;
      while (j >= 0 && array[j] > key) {
         array[j + 1] = array[j];
         --j;
      }
      array[j + 1] = key;
   }
}

class RegressionTable {
   public:
      RegressionTable(int arr[], int s, int lower, int upper, double mult, int divs);
      RegressionTable(int arr[], int s);
      void sort(void);
      int find(int key);
      void printTable(void);
      void showSize(void);
   private:
      void createTable(void);
      inline unsigned int resolve(int n);
      int * array;
      int * table;
      int * tableSize;
      int size;
      int lowerBound;
      int upperBound;
      int divisions;
      int divisionSize;
      int newSize;
      double multiplier;
};

RegressionTable::RegressionTable(int arr[], int s) {
   array = arr;
   size = s;
   multiplier = 1.35;
   divisions = sqrt(size);
   upperBound = INT_MIN;
   lowerBound = INT_MAX;
   for (int i = 0; i < size; ++i) {
      if (array[i] > upperBound)
         upperBound = array[i];
      if (array[i] < lowerBound)
         lowerBound = array[i];
   }
   createTable();
}

RegressionTable::RegressionTable(int arr[], int s, int lower, int upper, double mult, int divs) {
   array = arr;
   size = s;
   lowerBound = lower;
   upperBound = upper;
   multiplier = mult;
   divisions = divs;
   createTable();
}

void RegressionTable::showSize(void) {
   int bytes = sizeof(*this);
   bytes = bytes + sizeof(int) * 2 * (divisions + 1);
}

void RegressionTable::createTable(void) {
   divisionSize = size / divisions;
   newSize = multiplier * double(size);
   table = new int[divisions + 1];
   tableSize = new int[divisions + 1];

   for (int i = 0; i < divisions; ++i) {
      table[i] = 0;
      tableSize[i] = 0;
   }

   for (int i = 0; i < size; ++i) {
      ++table[((array[i] - lowerBound) / divisionSize) + 1];
   }

   for (int i = 1; i <= divisions; ++i) {
      table[i] += table[i - 1];
   }
   table[0] = 0;

   for (int i = 0; i < divisions; ++i) {
      tableSize[i] = table[i + 1] - table[i];
   }
}

int RegressionTable::find(int key) {
   double temp = multiplier;
   multiplier = 1;

   int minIndex = table[(key - lowerBound) / divisionSize];
   int maxIndex = minIndex + tableSize[key / divisionSize];
   int guess = resolve(key);
   double t;
   while (array[guess] != key) {
      // uncomment this line if you want to see where it is searching.
      //cout << "Regression Guessing " << guess << ", not there." << endl;
      if (array[guess] < key) {
         minIndex = guess + 1;
      }
      if (array[guess] > key) {
         maxIndex = guess - 1;
      }
      if (array[minIndex] > key || array[maxIndex] < key) {
         return -1;
      }
      t = ((double)key - array[minIndex]) / ((double)array[maxIndex] - array[minIndex]);
      guess = minIndex + t * (maxIndex - minIndex);
   }

   multiplier = temp;

   return guess;
}

inline unsigned int RegressionTable::resolve(int n) {
   float temp;
   int subDomain = (n - lowerBound) / divisionSize;
   temp = n % divisionSize;
   temp /= divisionSize;
   temp *= tableSize[subDomain];
   temp += table[subDomain];
   temp *= multiplier;
   return (unsigned int)temp;
}

void RegressionTable::sort(void) {
   int * out = new int[int(size * multiplier)];
   bool * used = new bool[int(size * multiplier)];
   int higher, lower;
   bool placed;

   for (int i = 0; i < size; ++i) {

      /* Figure out where to put the darn thing */
      higher = resolve(array[i]);
      lower = higher - 1;

      if (higher > newSize) {
         higher = size;
         lower = size - 1;
      } else if (lower < 0) {
         higher = 0;
         lower = 0;
      }
      placed = false;
      while (!placed) {
         if (higher < size && !used[higher]) {
            out[higher] = array[i];
            used[higher] = true;
            placed = true;
         } else if (lower >= 0 && !used[lower]) {
            out[lower] = array[i];
            used[lower] = true;
            placed = true;
         }
         --lower;
         ++higher;
      }
   }
   int index = 0;
   for (int i = 0; i < size * multiplier; ++i) {
      if (used[i]) {
         array[index] = out[i];
         ++index;
      }
   }

   insertionSort(array, size);
}

А тут еще функции обычного поиска:

#include <iostream>
using namespace std;

int binarySearch(int array[], int start, int end, int key) {
   // Determine the search point.
   int searchPos = (start + end) / 2;
   // If we crossed over our bounds or met in the middle, then it is not here.
   if (start >= end)
      return -1;
   // Search the bottom half of the array if the query is smaller.
   if (array[searchPos] > key)
      return binarySearch (array, start, searchPos - 1, key);
   // Search the top half of the array if the query is larger.
   if (array[searchPos] < key)
      return binarySearch (array, searchPos + 1, end, key);
   // If we found it then we are done.
   if (array[searchPos] == key)
      return searchPos;
}

int binarySearch(int array[], int size, int key) {
   return binarySearch(array, 0, size - 1, key);
}

int interpolationSearch(int array[], int size, int key) {
   int guess = 0;
   double t;
   int minIndex = 0;
   int maxIndex = size - 1;
   while (array[guess] != key) {

      t = ((double)key - array[minIndex]) / ((double)array[maxIndex] - array[minIndex]);
      guess = minIndex + t * (maxIndex - minIndex);

      if (array[guess] < key) {
         minIndex = guess + 1;
      }
      if (array[guess] > key) {
         maxIndex = guess - 1;
      }
      if (array[minIndex] > key || array[maxIndex] < key) {
         return -1;
      }
   }

   return guess;
}

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

    #include <iostream>
    #include <iomanip>
    #include <cstdlib>
    #include <ctime>
    #include "regression.h"
    #include "search.h"
    using namespace std;

    void randomizeArray(int array[], int size) {
       for (int i = 0; i < size; ++i) {
          array[i] = rand() % size;
       }
    }

    int main(int argc, char * argv[]) {

       int size = 100000;
       string arg;
       if (argc > 1) {
          arg = argv[1];
          size = atoi(arg.c_str());
       }
       srand(time(NULL));
       int * array;
       cout << "Creating Array Of Size " << size << "...\n";
       array = new int[size];

       randomizeArray(array, size);
       cout << "Sorting Array...\n";
       RegressionTable t(array, size, 0, size*2.5, 1.5, size);
       //RegressionTable t(array, size);
       t.sort();
       int trials = 10000000;
       int start;

       cout << "Binary Search...\n";
       start = clock();
       for (int i = 0; i < trials; ++i) {
          binarySearch(array, size, i % size);
       }
       cout << clock() - start << endl;

       cout << "Interpolation Search...\n";
       start = clock();
       for (int i = 0; i < trials; ++i) {
          interpolationSearch(array, size, i % size);
       }
       cout << clock() - start << endl;

       cout << "Regression Search...\n";
       start = clock();
       for (int i = 0; i < trials; ++i) {
          t.find(i % size);
       }
       cout << clock() - start << endl;

       return 0;
}

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

Я скомпилировал основной с g ++ на Ubuntu.

3 голосов
/ 23 мая 2013

Сначала посмотрите на данные и выясните, можно ли получить большой выигрыш с помощью метода, специфичного для данных, по общему методу.

Для больших статических отсортированных наборов данных вы можете создать дополнительный индекс для обеспечения частичной локализации голубей на основена количество памяти, которую вы готовы использовать.Например, скажем, мы создаем двумерный массив диапазонов 256x256, который мы заполняем начальными и конечными позициями в массиве поиска элементов с соответствующими старшими байтами.Когда мы приходим к поиску, мы используем старшие байты ключа, чтобы найти диапазон / подмножество массива, в котором мы должны искать.Если бы у нас было ~ 20 сравнений в нашем бинарном поиске 100 000 элементов O (log2 (n)), мы теперь сократились до ~ 4 комарисон для 16 элементов, или O (log2 (n / 15)).Стоимость памяти здесь составляет около 512k

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

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

3 голосов
/ 24 января 2011

Один из подходов к этому - использовать компромисс между пространством и временем. Есть множество способов сделать это. Крайний способ состоит в том, чтобы просто создать массив с максимальным размером, являющимся максимальным значением отсортированного массива. Инициализируйте каждую позицию с индексом в sortedArray. Тогда поиск будет просто O (1).

Следующая версия, однако, может быть немного более реалистичной и, возможно, полезной в реальном мире. Он использует вспомогательную структуру, которая инициализируется при первом вызове. Он отображает пространство поиска на меньшее пространство, разделив его на число, которое я вытащил из воздуха без особых испытаний. Он хранит индекс нижней границы для группы значений в sortedArray на вспомогательной карте. Фактический поиск делит число toFind на выбранный делитель и извлекает суженные границы sortedArray для обычного двоичного поиска.

Например, если отсортированные значения находятся в диапазоне от 1 до 1000, а делитель равен 100, тогда массив поиска может содержать 10 «секций». Чтобы найти значение 250, нужно разделить его на 100, чтобы получить целочисленную позицию индекса 250/100 = 2. map[2] будет содержать индекс sortedArray для значений 200 и больше. map[3] будет иметь позицию индекса со значениями 300 и больше, обеспечивая тем самым меньшую ограничивающую позицию для обычного двоичного поиска. Остальная часть функции является точной копией вашей функции двоичного поиска.

Инициализация карты помощника может быть более эффективной при использовании бинарного поиска для заполнения позиций, а не простого сканирования, но это единовременная стоимость, поэтому я не стал ее проверять. Этот механизм хорошо работает для данных тестовых чисел, которые распределены равномерно. Как написано, было бы не так хорошо, если бы распределение было не ровным. Я думаю, что этот метод может быть использован с поиском значений с плавающей запятой тоже. Однако экстраполировать его на общие ключи поиска может быть сложнее. Например, я не уверен, какой метод был бы для символьных ключей данных. Для поиска границ индекса потребуется какой-то вид поиска (хэш) O (1), который сопоставляется с конкретной позицией массива. Сейчас мне неясно, какой будет эта функция или она существует.

Я довольно быстро запустил настройку вспомогательной карты в следующей реализации. Это не красиво, и я не уверен на 100%, что это правильно во всех случаях, но это действительно показывает идею. Я запустил его с помощью теста отладки, чтобы сравнить результаты с вашей существующей функцией binarySearch, чтобы убедиться, что она работает правильно.

Ниже приведены примеры номеров:

100000 * 10000 : cycles binary search          = 10197811
100000 * 10000 : cycles interpolation uint64_t = 9007939
100000 * 10000 : cycles interpolation float    = 8386879
100000 * 10000 : cycles binary w/helper        = 6462534

Вот простая и быстрая реализация:

#define REDUCTION 100  // pulled out of the air
typedef struct {
    int init;  // have we initialized it?
    int numSections;
    int *map;
    int divisor;
} binhelp;

int binarySearchHelp( binhelp *phelp, int sortedArray[], int toFind, int len)
{
    // Returns index of toFind in sortedArray, or -1 if not found
    int low;
    int high;
    int mid;

    if ( !phelp->init && len > REDUCTION ) {
        int i;
        int numSections = len / REDUCTION;
        int divisor = (( sortedArray[len-1] - 1 ) / numSections ) + 1;
        int threshold;
        int arrayPos;

        phelp->init = 1;
        phelp->divisor = divisor;
        phelp->numSections = numSections;
        phelp->map = (int*)malloc((numSections+2) * sizeof(int));
        phelp->map[0] = 0;
        phelp->map[numSections+1] = len-1;
        arrayPos = 0;
        // Scan through the array and set up the mapping positions.  Simple linear
        // scan but it is a one-time cost.
        for ( i = 1; i <= numSections; i++ ) {
            threshold = i * divisor;
            while ( arrayPos < len && sortedArray[arrayPos] < threshold )
                arrayPos++;
            if ( arrayPos < len )
                phelp->map[i] = arrayPos;
            else
                // kludge to take care of aliasing
                phelp->map[i] = len - 1;
        }
    }

    if ( phelp->init ) {
        int section = toFind / phelp->divisor;
        if ( section > phelp->numSections )
            // it is bigger than all values
            return -1;

        low = phelp->map[section];
        if ( section == phelp->numSections )
            high = len - 1;
        else
            high = phelp->map[section+1];
    } else {
        // use normal start points
        low = 0;
        high = len - 1;
    }

    // the following is a direct copy of the Kriss' binarySearch
    int l = sortedArray[low];
    int h = sortedArray[high];

    while (l <= toFind && h >= toFind) {
        mid = (low + high)/2;

        int m = sortedArray[mid];

        if (m < toFind) {
            l = sortedArray[low = mid + 1];
        } else if (m > toFind) {
            h = sortedArray[high = mid - 1];
        } else {
            return mid;
        }
    }

    if (sortedArray[low] == toFind)
        return low;
    else
        return -1; // Not found
}

Вспомогательная структура должна быть инициализирована (и освобождена память):

    help.init = 0;
    unsigned long long totalcycles4 = 0;
    ... make the calls same as for the other ones but pass the structure ...
        binarySearchHelp(&help, arr,searched[j],length);
    if ( help.init )
        free( help.map );
    help.init = 0;
3 голосов
/ 21 января 2011

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

2 голосов
/ 21 января 2011

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

int newSearch(int sortedArray[], int toFind, int len) 
{
    // Returns index of toFind in sortedArray, or -1 if not found
    int low = 0;
    int high = len - 1;
    int mid;

    int l = sortedArray[low];
    int h = sortedArray[high];

    while (l < toFind && h > toFind) {
        mid = low + ((float)(high - low)*(float)(toFind - l))/(1+(float)(h-l));

        int m = sortedArray[mid];

        if (m < toFind) {
            l = sortedArray[low = mid + 1];
        } else if (m > toFind) {
            h = sortedArray[high = mid - 1];
        } else {
            return mid;
        }
    }

    if (l == toFind)
        return low;
    else if (h == toFind)
        return high;
    else
        return -1; // Not found
}
0 голосов
/ 04 июня 2018

Реализация бинарного поиска, который использовался для сравнений, может быть улучшена.Основная идея состоит в том, чтобы изначально «нормализовать» диапазон, чтобы цель всегда была> минимальной и <максимальной после первого шага.Это увеличивает размер дельты завершения.Это также имеет эффект специальных целей кожуха, которые меньше, чем первый элемент отсортированного массива или больше, чем последний элемент отсортированного массива.Ожидайте примерно 15% улучшение времени поиска.Вот как может выглядеть код в C ++. </p>

int binarySearch(int * &array, int target, int min, int max)
{ // binarySearch
  // normalize min and max so that we know the target is > min and < max
  if (target <= array[min]) // if min not normalized
  { // target <= array[min]
      if (target == array[min]) return min;
      return -1;
  } // end target <= array[min]
  // min is now normalized

  if (target >= array[max]) // if max not normalized
  { // target >= array[max]
      if (target == array[max]) return max;
      return -1;
  } // end target >= array[max]
    // max is now normalized

  while (min + 1 < max)
  { // delta >=2
    int tempi = min + ((max - min) >> 1); // point to index approximately in the middle between min and max
    int atempi = array[tempi]; // just in case the compiler does not optimize this
    if (atempi > target)max = tempi; // if the target is smaller, we can decrease max and it is still normalized        
    else if (atempi < target)min = tempi; // the target is bigger, so we can increase min and it is still normalized        
        else return tempi; // if we found the target, return with the index
        // Note that it is important that this test for equality is last because it rarely occurs.
  } // end delta >=2
  return -1; // nothing in between normalized min and max
} // end binarySearch
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...