Интерполяционный поиск — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
(Пока так)
Строка 4: Строка 4:
 
== Алгоритм ==
 
== Алгоритм ==
 
[[Файл:Search.jpg|thumb|300px|Нахождение разделительного элемента]]
 
[[Файл:Search.jpg|thumb|300px|Нахождение разделительного элемента]]
Пусть <tex> a </tex> {{---}} отсортированный массив чисел из <tex> n </tex> чисел, <tex> x </tex> {{---}} значение, которое нужно найти. Сам алгоритм похож на [[Целочисленный двоичный поиск|двоичный поиск]], однако есть существенное отличие: если известно, что <tex> x </tex> лежит между <tex> a_l </tex> и <tex> a_r </tex>, то следующая проверка выполняется примерно на расстоянии <tex dpi = "170"> \frac{x - a_l}{a_r - a_l} </tex> от <tex> l </tex>.
+
Пусть <tex> a </tex> {{---}} отсортированный массив чисел из <tex> n </tex> чисел, <tex> x </tex> {{---}} значение, которое нужно найти. Поиск происходит подобно [[Целочисленный двоичный поиск|двоичному поиску]], но вместо деления области поиска на две примерно равные части, интерполирующий поиск производит оценку новой области поиска по расстоянию между ключом и текущим значением элемента. Если известно, что <tex> x </tex> лежит между <tex> a_l </tex> и <tex> a_r </tex>, то следующая проверка выполняется примерно на расстоянии <tex dpi = "170"> \frac{x - a_l}{a_r - a_l} </tex> от <tex> l </tex>.
  
 
=== Псевдокод ===
 
=== Псевдокод ===
  
 
  <code>
 
  <code>
  int interpolation_search(double* a, int n, double x)
+
  interpolationSearch(n, x):
{
+
   l = 0; // левая граница поиска (будем считать, что элементы массива нумеруются с нуля)
   int l = 0;
+
   r = n - 1; // правая граница поиска
   int r = n - 1;
 
  int m;
 
 
   
 
   
   while (a[l] <= x && x <= a[r])
+
   while a[l] <= x && x <= a[r]
  {
+
     m = l + (x - a[l]) / (a[r] - a[l]) * (r - l); // элемент, с которым будем проводить сравнение
     m = l + (x - a[l]) / (a[r] - a[l]) * (r - l);
+
     if a[m] == x
+
       result = m;
     if (a[m] == x)
+
     if a[m] < x
       return m;
 
     if (a[m] < x)
 
 
       l = m + 1;
 
       l = m + 1;
 
     else
 
     else
 
       r = m - 1;
 
       r = m - 1;
  }
 
 
   
 
   
   if (a[l] == x)
+
   if a[l] == x
     return l;
+
     result = l;
 
   else
 
   else
     return -1; // not found
+
     result = -1; // not found
}
 
 
  </code>
 
  </code>
  
Строка 38: Строка 32:
  
 
При "плохих" исходных данных (например, при экспоненциальном возрастании элементов) время работы может ухудшиться до <tex> O(n) </tex>.
 
При "плохих" исходных данных (например, при экспоненциальном возрастании элементов) время работы может ухудшиться до <tex> O(n) </tex>.
 +
 +
Эксперименты показали, что интерполяционный поиск не настолько снижает количество выполняемых сравнений, чтобы компенсировать требуемое для дополнительных вычислений время (пока таблица не очень велика). Кроме того, типичные таблицы недостаточно случайны, да и разница между значениями <tex>\log \log n</tex> и <tex>\log n</tex> становится значительной только при очень больших <tex>N</tex>. На практике при поиске в больших файлах оказывается выгодным на ранних стадиях применять интерполяционный поиск, а затем, когда диапазон существенно уменьшится, переходить к двоичному.
  
 
== Литература ==
 
== Литература ==
Д.Э. Кнут: [http://books.google.com/books?id=92rW-nktlbgC&pg=PA452&lpg=PA453&ots=jChsP2sutg&dq=%D0%BA%D0%BD%D1%83%D1%82+%D0%B8%D0%BD%D1%82%D0%B5%D1%80%D0%BF%D0%BE%D0%BB%D1%8F%D1%86%D0%B8%D0%BE%D0%BD%D0%BD%D1%8B%D0%B9+%D0%BF%D0%BE%D0%B8%D1%81%D0%BA&hl=ru&ie=windows-1251&output=html Искусство программирования (том 3)] <br/>
+
Д.Э. Кнут: [http://books.google.com/books?id=92rW-nktlbgC&pg=PA452&lpg=PA453&ots=jChsP2sutg&dq=%D0%BA%D0%BD%D1%83%D1%82+%D0%B8%D0%BD%D1%82%D0%B5%D1%80%D0%BF%D0%BE%D0%BB%D1%8F%D1%86%D0%B8%D0%BE%D0%BD%D0%BD%D1%8B%D0%B9+%D0%BF%D0%BE%D0%B8%D1%81%D0%BA&hl=ru&ie=windows-1251&output=html Искусство программирования (том 3)]
 +
 
 
Wikipedia: [http://en.wikipedia.org/wiki/Interpolation_search Interpolation search]
 
Wikipedia: [http://en.wikipedia.org/wiki/Interpolation_search Interpolation search]
 +
 +
Wikipedia: [http://ru.wikipedia.org/wiki/%D0%98%D0%BD%D1%82%D0%B5%D1%80%D0%BF%D0%BE%D0%BB%D0%B8%D1%80%D1%83%D1%8E%D1%89%D0%B8%D0%B9_%D0%BF%D0%BE%D0%B8%D1%81%D0%BA Интерполирующий поиск]
  
 
[[Категория: Дискретная математика и алгоритмы]]
 
[[Категория: Дискретная математика и алгоритмы]]
 
[[Категория: Алгоритмы поиска]]
 
[[Категория: Алгоритмы поиска]]

Версия 12:25, 17 мая 2012

Идея

Рассмотрим задачу: найти слово в словаре. Если оно начинается на букву "А", то никто не будет искать его в середине, а откроет словарь ближе к началу. В чём разница между алгоритмом человека и другими? Отличие заключается в том, что алгоритмы вроде двоичного поиска не делают различий между "немного больше" и "существенно больше".

Алгоритм

Нахождение разделительного элемента

Пусть [math] a [/math] — отсортированный массив чисел из [math] n [/math] чисел, [math] x [/math] — значение, которое нужно найти. Поиск происходит подобно двоичному поиску, но вместо деления области поиска на две примерно равные части, интерполирующий поиск производит оценку новой области поиска по расстоянию между ключом и текущим значением элемента. Если известно, что [math] x [/math] лежит между [math] a_l [/math] и [math] a_r [/math], то следующая проверка выполняется примерно на расстоянии [math] \frac{x - a_l}{a_r - a_l} [/math] от [math] l [/math].

Псевдокод


interpolationSearch(n, x):
  l = 0; // левая граница поиска (будем считать, что элементы массива нумеруются с нуля)
  r = n - 1; // правая граница поиска

  while a[l] <= x && x <= a[r]
    m = l + (x - a[l]) / (a[r] - a[l]) * (r - l); // элемент, с которым будем проводить сравнение
    if a[m] == x
      result = m;
    if a[m] < x
      l = m + 1;
    else
      r = m - 1;

  if a[l] == x
    result = l;
  else
    result = -1; // not found

Время работы

Асимптотически интерполяционный поиск превосходит по своим характеристикам бинарный. Если ключи распределены случайным образом, то за один шаг алгоритм уменьшает количество проверяемых элементов с [math] n [/math] до [math] \sqrt n [/math]. То есть, после [math]k[/math]-ого шага количество проверяемых элементов уменьшается до [math]n^{\frac{1}{2^k}}[/math]. Значит, остаётся проверить только 2 элемента (и закончить на этом поиск), когда [math]\frac{1}{2^k} = log_{n}2 = \frac{1}{log_{2}n} [/math]. Из этого вытекает, что количество шагов, а значит, и время работы составляет [math]O(\log \log n)[/math].

При "плохих" исходных данных (например, при экспоненциальном возрастании элементов) время работы может ухудшиться до [math] O(n) [/math].

Эксперименты показали, что интерполяционный поиск не настолько снижает количество выполняемых сравнений, чтобы компенсировать требуемое для дополнительных вычислений время (пока таблица не очень велика). Кроме того, типичные таблицы недостаточно случайны, да и разница между значениями [math]\log \log n[/math] и [math]\log n[/math] становится значительной только при очень больших [math]N[/math]. На практике при поиске в больших файлах оказывается выгодным на ранних стадиях применять интерполяционный поиск, а затем, когда диапазон существенно уменьшится, переходить к двоичному.

Литература

Д.Э. Кнут: Искусство программирования (том 3)

Wikipedia: Interpolation search

Wikipedia: Интерполирующий поиск