Участник:Shovkoplyas Grigory — различия между версиями

Материал из Викиконспекты
Перейти к: навигация, поиск
Строка 8: Строка 8:
 
=== Псевдокод ===
 
=== Псевдокод ===
 
<code style = "display: inline-block;">
 
<code style = "display: inline-block;">
  function interpolation_search(array, key) //array должен быть отсортирован
+
  function interpolation_search(a : int[], key : int) //a должен быть отсортирован
 
   left = 0 // левая граница поиска (будем считать, что элементы массива нумеруются с нуля)
 
   left = 0 // левая граница поиска (будем считать, что элементы массива нумеруются с нуля)
   right = array.length - 1 // правая граница поиска
+
   right = a.length - 1 // правая граница поиска
 
   
 
   
   while array[left] <tex> \le </tex> key and key <tex> \le </tex> array[right]
+
   while a[left] <tex> \le </tex> key and key <tex> \le </tex> a[right]
     mid = left + (key - array[left]) / (array[right] - array[left]) * (right - left) // индекс элемента, с которым будем проводить сравнение
+
     mid = left + (key - a[left]) / (a[right] - a[left]) * (right - left) // индекс элемента, с которым будем проводить сравнение
     if array[mid] == key
+
     if a[mid] == key
 
       return mid
 
       return mid
     if array[mid] < key
+
     if a[mid] < key
 
       left = mid + 1
 
       left = mid + 1
 
     else
 
     else
 
       right = mid - 1
 
       right = mid - 1
 
   
 
   
   if array[left] == key
+
   if a[left] == key
 
     return left
 
     return left
 
   else
 
   else

Версия 14:05, 15 июня 2014

Идея

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

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

Алгоритм

Пусть [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} \cdot[/math] [math] (r - l) [/math] от [math] l [/math].

Псевдокод

function interpolation_search(a : int[], key : int) //a должен быть отсортирован
  left = 0 // левая граница поиска (будем считать, что элементы массива нумеруются с нуля)
  right = a.length - 1 // правая граница поиска

  while a[left] [math] \le [/math] key and key [math] \le [/math] a[right]
    mid = left + (key - a[left]) / (a[right] - a[left]) * (right - left) // индекс элемента, с которым будем проводить сравнение
    if a[mid] == key
      return mid
    if a[mid] < key
      left = mid + 1
    else
      right = mid - 1

  if a[left] == key
    return left
  else
    return -1 // если такого элемента в массиве нет

Время работы

Асимптотически интерполяционный поиск превосходит по своим характеристикам бинарный. Если ключи распределены случайным образом, то за один шаг алгоритм уменьшает количество проверяемых элементов с [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: Интерполирующий поиск