Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
МУ_ЛР_СДА2010Ukr.doc
Скачиваний:
13
Добавлен:
16.08.2019
Размер:
688.13 Кб
Скачать

4 Алгоритми пошуку

4.1 Мета роботи – ознайомлення з різними алгоритмами пошуку елемента й послідовностей елементів [3, c. 757-785; 5, том 3, c.247-355], порівняння застосовності алгоритмів на різних завданнях й їхніх часових характеристик.

4.2 Методичні вказівки з організації самостійної роботи студентів

4.2.1 Пошук елементу в послідовності

Завдання пошуку полягає в тому, що маємо ланцюжок однакових елементів, у якому треба знайти перше або усі входження шуканого елементу або їх підпослідовності.

Найпростіший варіант пошуку елементу – це послідовне проглядання усіх елементів сукупності на предмет ідентичності із зразком. Вісь алгоритм такого пошуку.

Proc SimpleSearch(A[],elem,index_of_elem)

index_of_elem = -1

for i= 1 To Length(A) do

if A[i] = elem then

index_of_elem = i

break

endif

endfor

End SimpleSearch

Цей алгоритм дуже неефективний, якщо кількість елементів в А велика, а elem розташований у кінці, або його зовсім німа в послідовності А.

Бінарний пошук. Якщо послідовність упорядкована, то можна використовувати так званий бінарний пошук. Його ідея проста: ділимо послідовність навпіл і перевіряємо, чи входить елемент, що розшукується, до лівої чи правої половини (чи належить він діапазонам елементів, що належать їм), якщо так, то пошук продовжується аналогічним шляхом в обраній половині, якщо ні, то повертається негативний результат. Алгоритм бінарного пошуку наведень нижче:

Proc BinarySearch (A[], LowBound, UpBound, elem, index_of_elem)

Index_of_elem = -1

while LowBound <= UpBound

M = (LowBound + UpBound)/2

if A[M] = elem then

index_of_elem = M

else if (elem < A[M]) then

UpBound = M - 1

else (elem > A[M]) then

LowBound = M + 1

endif

endif

endwhile

End BinarySearch

Треба відзначити, що такий самий підхід (з тією ж самою часовою ефективністю) лежить в основі пошуку елемента в бінарному дереві. Алі бінарне дерево відрізняється прискореним, у порівнянні із зберіганням у масиві (на суміжній пам’яті ), часом додавання та вилучення елементу.

Інтерполяційний пошук. Однак, якщо справа йде про пошук слова в словнику, Ви навряд будете розкривати його спочатку на середині, потім знов на середині отриманого діапазону. Скоріше Ви розкриєте словник на частини, пропорційній положенню першої букві слова в алфавіті, за яким розташовані слова в словнику. Так діє інтерполяційний пошук.

Якщо відомо, що elem(ключ, який треба знайти) лежить між Al значення елементу на позиції l (поточна найлівіша) та Au значення елементу на позиції u (поточна найправіша), то наступну спробу робимо на відстані (u-l)(A-Al)/(Au-Al) від l, припускаючи, що ключі є числами, що зростають приблизно в арифметичній прогресії.

Весь алгоритм інтерполяційного пошуку:

Proc InterpSearch(A[], elem, index_of_elem) Index_of_elem = -1 L = 1 U = Length(A) while U>=L do i=L+(U-L)*(elem-A[L])/(A[U]-A[L]); if elem<A[i] then U=i-1 else if elem>A[i] then L=i+1; else index_of_elem = i break endif endwhile End InterpSearch

Пошук у хеш-таблиці. Існує ще один ефективний метод реалізації словників, який називається хешуванням. Цей метод потребує фіксованого часу (у середньому) на виконання операторів вставки, пошуку та вилучення елементу. Існує два способи хешування: відкрите (або зовнішнє) та закрите (або внутрішнє) хешування. Відкрите хешування на відміну від закритого може зберігати нескінченну кількість елементів. Закрите хешування використовує обмежений простір для зберігання елементів, тим самим обмежуючи їх кількість.

Відкрите хешування. На рис. 4.1 показана загальна модель зберігання елементів при відкритому хешуванні (вона схожа на зберігання елементів під час кишенькового сортування).

Потенційно нескінченна кількість елементів розбивається на скінченну множину класів. Для В класів, пронумерованих від 0 до В-1 вибирається та обчислюється хеш-функція h(x), що для шкірного ключа х приймає цілочисельне хеш-значення в інтервалі від 0 до В-1, що і відповідає класу, якому належить елемент з ключем х. Таблиця сегментів – це, наприклад, масив, що містить початки В списків, кожен елемент x і-го списку – це елемент, для якого h(x) = i. Якщо сегменти приблизно однакового розміру та множина розкиданих по них елементів має розмір N, тоді середня довжина кожного списку буде N/B. Якщо можна оцінити N та зробити В~N, тоді в кожному списку буде один-два елементи, а час виконання операторів вставки, пошуку та вилучення елементу множини N буде фіксованим, незалежним від N та В.

Рисунок 4.1 - Організація даних у відкритому хешуванні

Функцію h(x) треба обирати так, щоб вона приблизно рівно розподіляла елементи по В сегментах. Приклад простої h(x) для рядкового типу ключа х наведено нижче.

function h(x):0..B-1 begin sum = 0 for i = 1 to 10 do sum = sum + x[i] return sum % B end.

Закрите хешування. Під час закритого хешування в таблиці сегментів зберігаються не початки списків, а безпосередньо самі елементи словника (рис. 4.2). Для такого хешування застосовується методика повторного хешування: якщо сегмент, номер якого поверне h(x), вже зайнятий (такий випадок називається колізією) , то виконуються послідовні спроби розмістити елемент у сегменти h1(x), h2(x), …, hn(x), допоки не буде знайдено вільний. Якщо місця нема, то, відповідно, таблиця заповнена та розмістити елемент вже неможливо. Функція hi(x) може виглядати як hi(x) = (h(x)+i)%B. Наприклад, якщо h(x)= 3, та третій сегмент вже зайнято, то виконується спроба розташувати елемент у сегментах 4,5,6…B-1...

Кожна незайнята чарунка таблиці сегментів повинна бути помічена empty, або deleted, останнє означає, що елемент був у ній, але вже видалений.

Рисунок 4.2 - Частково заповнена таблиця сегментів

Під час пошуку елементу послідовно перевіряються елементи h(x), h1(x), h2(x), …, поки не буде знайдений елемент, або сегмент з поміткою empty.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]