Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Мой курсач готовый.doc
Скачиваний:
4
Добавлен:
06.11.2018
Размер:
272.9 Кб
Скачать

3. Интерполяционный поиск

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

Интерполяция - это процесс предсказания неизвестных значений на основе имеющихся. В данном случае вы используете индексы известных значений в списке, чтобы определить, какой индекс должно иметь искомое значение. Предположим, что вы имеете такой же список, как на рис. 10.2. Этот список содержит 20 элементов со значениями от 1 до 70. Допустим, что вы хотите найти в этом списке элемент со значением 44. Значение 44 составляет 64% размера диапазона от 1 до 70. Если считать, что значения элементов распределены равномерно, то искомый элемент, предположительно, будет находиться в позиции, составляющей 64% от размера списка - то есть в позиции с индексом 13.

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

Рисунок 3 – Интерполяционный поиск значения 44

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

Middle := Round(min + ((target – list^[min]) *

((max - min)/(list^[max] – list^[min]))));

Эта операция помещает значение элемента middle между min и max, что соответствует положению искомого элемента между list^[min] и list^ [max]. Если искомый элемент близок к list^[min], то разность target – list^ [min] близка к 0. Тогда middle = min + 0 близко к 0, поэтому значение middle почти равно min. Можно ожидать, что индекс элемента будет близок к min, если его значение почти равно list^[min].

Аналогично, если искомый элемент находится рядом с list^[max] разность target – list^[min] практически такая же, как и разность list^[max] – list^ [min]. Их частное близко к единице, и соотношение выглядит почти как middle = min + (max - min), что упрощается до middle = max. Смысл этого соотношения заключается в том, что если значение элемента близко к list^[max], то его индекс практически равен max.

Когда программа вычислит значение middle, она сравнивает значение элемента в этой позиции с искомым так же, как и при двоичном поиске. Если элемент равен искомому, то программа завершает работу. Если искомый элемент меньше, то программа устанавливает max = middle - 1 и продолжает искать в списке меньшие элементы. Если искомый элемент больше найденного, программа устанавливает min = middle + 1 и продолжает искать в списке большие элементы.

Обратите внимание, что соотношение, которое вычисляет новое значение middle,делится на (list^[max] - list^min]) .Если list^[min] = list^[max],то происходит попытка деления на нуль и программа аварийно завершит работу. Это может случаться, если в списке имеется два идентичных значения. Поскольку алгоритм следует условию min < = искомый индекс < = max, эта проблема может возник- возникнуть, если значение min возросло, а значение max уменьшилось до уровня min = max.

Чтобы справиться с этой проблемой, программа перед делением проверяет условие list^[min] = list^[max]. Если условие выполняется, значит, осталось проверить только одно значение. Программа просто проверяет его на равенство искомому.

Еще одна тонкость заключается в том, что вычисленное значение middle не всегда находится между min и max. Самый простой случай, когда это происходит, - это когда искомый элемент лежит за пределами диапазона значений списка. Предположим, что вы ищете значение 300 в списке 100,150,200. Первый раз, когда программа вычисляет средний элемент, min = l, a max = 3.Тогда middle = 1 + (300 –List[1]) * (3 -1) / (List[3] -List[l] ) = 1 + (300- 100) *2 / (200- 100) =5. Индекс 5 находится не только за пределами диапазона min <= искомый индекс <= max, но также за пределами границ массива. Если программа попробует обратиться к элементу List^[ 5 ], то она аварийно завершит работу с сообщением об ошибке Subscript out of range.

Подобная проблема может возникать, если значения между min и max распределены очень неравномерно. Предположим, что надо найти значение 100 в списке О, 1, 2,199, 200. При первом вычислении значения переменной middle вы получите 1+ (100 - 0)* (5-1) / (200-0) =3. Затем она сравнивает list* [3] с искомым элементом 100. Так как List* [3 ] = 2 меньше, чем 100, программа устанавливает min = middle +1 = 4.

Затем программа вычисляет значение middle -middle = 4+ (100 -199) * (5-4) / (200-199) =-98. Значение -98 лежит за пределами диапазона min <= искомый индекс <= max и далеко за границами массива.

Если рассмотреть вычисление среднего значения, то можно увидеть два варианта, при которых новое значение может быть меньше min или больше max. Предположим, что middle меньше min.

min + ((target – list^[min]) * ((max- min) / (list* [max] – list^[min])) )< min

Вычитая min из обеих частей, получим:

(target – list^[min]) * ((max-min)/(list^[max] – list^[min]) < 0

Поскольку max >= min, разность (max-min) должна быть больше 0. Посколькy list^[max] >=list*[min],разность (list^[max] –list^ [min]) тоже должна быть больше 0. Тогда единственный вариант, при котором все значение может быть меньше 0, только если разность (target – list^[min] ) меныне 0. Это означает, что искомое значение меньше, чем list^[min]. В этом случае искомый элемент не может находиться в списке, так как все записи со значением меньше list*[min] были уже устранены. Теперь предположим, что middle больше, чем max.

min + ((target-list^[min]) * ((max - min)/(list*[max] – list^[min]))) >max

Вычитая min из обеих частей, получим:

(target – list^[min])*(max-min) / (list^[max] – list^[min]) >max – min

Умножение обеих частей на (list* [max] - list^[min] ) / (max-min) приводит соотношение к виду:

target – List^[min] > List^[max] – list^[min]

И наконец, добавляя list* [min] к обеим частям, получаем:

target > List^[max]

Это означает, что искомое значение больше, чем list* [ max]. Следовательно, искомый элемент не может быть в списке, потому что все записи списка со значениями большими, чем list^[max], были уже устранены.

Объединяя эти результаты, получаем, что единственный вариант, при котором новое значение middle может быть вне диапазона от min до max, если искомое значение лежит вне диапазона от list*[min] до list*[max]. Алгоритм использует этот факт при каждом вычислении нового значения middle. Сначала он проверяет, лежит ли новое значение в диапазоне от min до max. Если нет, то искомого элемента нет в списке, и работа алгоритма завершена.

Следующий код показывает, как выполняет интерполяционный поиск программа Search:

function InterpolationSearch (target : Longint; List : PLongArray;

min, max : Longint) : Longint;

var

middle : Longint;

begin

while (min <= max) do

begin

// Предотвращение деления на нуль.

if (list^[min] = list^[max]) then

begin

// Это должен быть искомый элемент (если он есть в списке) .

if List[min] = target then

Result := min

else

Result := 0;

exit;

end;

// Вычисление точки деления.

middle := Round (min + ((target- list^[min]) *

((max-min) / (list^[max] – list^[min]) ) ) ) ;

// Удостовериться, что мы не вышли за пределы диапазона.

if ( (middle< min) or (middle > max) ) then

begin

// Элемента в списке нет.

Result := 0;

exit;

end;

if target = List [middle] then // Элемент найден.

begin

Result := middle;

exit;

end else if target < List [middle] then

// Перебор левой половины.

max := middle - 1

else

// Перебор правой половины.

min := middle + 1;

end; // Конец while (min <= max) do. . .

// Если мы достигли этой точки, то элемента в списке нет.

Result := 0;

end;

Двоичный поиск выполняется очень быстро, но интерполяционный поиск еще быстрее. В одном из тестов двоичный поиск занимал в 3 раза больше времени для нахождения значения в списке из 100 000 элементов. Эта разница была бы еще большей, если бы данные были сохранены на жестком диске или другом устройстве, с которого информация считывается медленно. Хотя алгоритм интерполяционного поиска тратит гораздо больше времени на вычисления, за счет меньшего числа обращений к диску он оказывается эффективнее.