- •Інформатика
- •Необчислювальні алгоритмы
- •Від автора
- •Створення алгоритму
- •Налагодження алгоритму
- •Допоміжні задачі
- •Поняття систем числення
- •Числова інформація Цілі числа
- •Дійсні числа
- •Текстова інформація Символи
- •Дерево. Бінарне дерево
- •If to nil then with t* do begin
- •Бінарний пошук Пошук діленням навпіл
- •Рекурсивний бінарний пошук
- •Пошук у рядку
- •Скінченні автомати Основні поняття
- •Пошук у мережі
- •Прямі методи сортування Сортування вибором
- •Сортування обміном
- •Шейкерне сортування
- •Сортування методом Шелла
- •Швидке сортування
- •Метод прямого злиття
- •Метод природного злиття
- •Сортування підрахунком
- •Цифрове сортування
- •Література
- •61012, М. Харків, вул. Енгельса, 11.
Бінарний пошук Пошук діленням навпіл
Кожен із вас мав нагоду шукати деяку інформацію у зви-чайній книжці чи в словнику. Різниця такого пошуку досить очевидна: у словнику вона впорядкована за алфавітом, а в книжці розташована в довільному порядку. Зрозуміло, що перший випадок розміщення інформації набагато зручніший. Як можна скористатися впорядкованістю інформації? Прига-даємо, як ми здійснюємо пошук у словнику:
1) відкриваємо словник у довільному місці й дивимося: якщо шукане слово знайдено, то пошук припиняемо, в іншому випадку переходимо до наступного пункту;
-
якщо слово знаходиться попереду, то друга частина словника нас уже не цікавить і ми відкриваємо його десь усередині першої частини і починаемо пошук так само, як і в пункті 1);
-
якщо слово знаходиться далі, то перша частина словника нас не цікавить і ми відкриваємо його десь усередині другої частини та починаемо пошук так само, як і в пункті 1).
Ми, фактично, записали алгоритм пошуку інформації в упо-рядкованій послідовності елементів. Він носить назву бінарно-го (двійкового) пошуку, або пошуку методом поділу навпіл.
Визначимося щодо нашої задачі в алгоритмічних термінах. Нам задано масив а(, і - 1, 2, ..., п, для елементів якого справджується умова: at < a, + 1, і - 1, 2, .... п - 1. Шуканий елемент позначимо х, а елемент масиву, відносно якого масив ділиться на дві частини, - через а\т].
110
Тепер алгоритм бінарного пошуку виглядатиме так:
-
Визначимо т.
-
Якщо а[т] = х, то пошук завершено і переходимо до п. 5, в іншому випадку переходимо до п. 3.
-
Якщо а[т] < х, то всі елементи з індексами, меншими за т, можна відкинути і перейти до п. 1.
-
Якщо а[т] > х, то всі елементи з індексами, більшими за т, можна відкинути і перейти до п. 1.
-
Вивести результат (фп].
Нам необхідно однозначно визначитися щодо елемента, від-носно якого відбувається поділ масиву на дві частини. Таким елементом може бути довільний елемент масиву, навіть визна-чений випадковим чином у проміжку [1; N]. Але зрозуміло, що наикращим варіантом буде вибір «центрального» елемента a[N + 1 div 2], оскільки при цьому в будь-якому разі буде від-кидатися половина масиву.
Ще однією домовленістю повинна бути визначеність щодо обставини «відкидання» правої чи лівої частини масиву. Щоце означав? Якщо на самому початку межами масиву є індекси 1 i N, то з кожним кроком цей проміжок звужується. При цьому або значения лівої межі переміщається вправо, або право! - вліво. Для того щоб бути однозначними також і в позна-ченні меж проміжку значень індексів елементів масиву, що розглядається, давайте ліву межу будемо позначати змінною L, а праву - R. У цих термінах «відкидання» лівої частини масиву відносно <i[N + 1 div 2] буде відповідати дії' L := (N + 1 div 2) + 1, правої - R := (N + 1 div 2) - 1.
Перш ніж переходити до створення алгоритму, поміркуємо ще над умовами його завершения, тобто над ознаками визна-чення результату пошуку. На кожному кроці ми визначаємо центральний елемент послідовності, відповідно до значения якого відбувається порівняння із шуканим елементом х. Тому, якщо значения шуканого елемента співпаде зі значениям елемента а[т], алгоритм пошуку можна припиняти. Для пе-ревірки цієї умови введемо логічну змінну flag, яка набуватиме значения true у випадку, коли на певному кроці пошуку ми натрапимо на шуканий елемент, тобто виконається умова а[т] = х.
Представимо наведений алгоритм бінарного пошуку мовою Pascal:
L := 1;R:=N; flag := false; while (L <= R) and not flag do begin
m:=(L+R)div2; if a[m] = x then flag := true
111
else if a[m]<x
then L := m + 1 else R := m - 1 end; if flag then writeln ('Шуканий елемент знайдено') else writeln ('Шуканий елемент не знайдено');
Чи не можна дещо вдосконалити цей алгоритм? Виявляеть-ся, можна, помінявши місцями вкладені умовні оператори: пе-ревірку на рівність шуканого елемента і «середнього» вико-нувати в останню чергу, оскільки вона при виконанні нашого алгоритму трапиться лише один раз і приведе до завершения алгоритму. А може статися і така ситуація, що ця умова жод-ного разу не виконається. Перевіряючи умову a[m] = х остан-ньою, ми таким чином запобігаємо зайвим перевіркам умов, які найчастіше не дають позитивного результату:
if a[m]<x then L := m + 1 else if a[m] > x
then R := m - 1
else flag := true;
Можна запропонувати інший варіант цього самого алгоритму, але з використанням повторения з післяумовою:
L:=1;R:=N; repeat
m:=(L+R)div2; if х > a[m]
then L := m + 1 else if x<a[m]
then R : = m - 1 until (a[m] = x) or (L = R); if a[m] = x then writeln ('Шуканий елемент знайдено')
else writeln ('Шуканий елемент не знайдено');
Суттєвішим удосконаленням алгоритму буде, як і при ліній-ному пошуку, таке, яке передбачило б логічне завершения алгоритму без зайвої перевірки умови not flag у першому варіанті та a[m] = х у другому. Адже виконання цих умов можливе лише один раз або навіть жодного разу не відбудеться нротягом виконання всього алгоритму. I справді, відмова від перевірки збі-гання значения шуканого елемента з поточним значениям ♦ центрального» елемента масиву значно покращуе виграш ефективності алгоритму: на кожному кроці втрати від швидко-го визначення результату виконання алгоритму значно менші, ніж переваги відмови від зайвих порівнянь.
Найефективніший алгоритм бінарного пошуку заданого елемента в упорядкованій послідовності виглядатиме так:
112
L:=1;R:=N; while (L<R) do begin
m:=(L + R)div2; if a[m] < x then L := m + 1 else R := m end; if a[R] = x then writeln ('Шуканий елементзнайдено')
else writeln ('Шуканий елемент не знайдено');
Залишилося лише проаналізувати умову визначення результату пошуку a[R] = х. Умовою виходу з циклу є L > R. Однак умова L > R ніколи не буде досяжною, оскільки проміжок [L; R] завжди зменшується за умови, що або L := m + 1, або R := т. При цьому завершения алгоритму відбудеться лише за умови L = R. Але зрозуміло, що виконання умови L = R зовсім не свідчить про те, що шуканий елемент знайдено. Це говорить лише про те, що ми зменшували проміжок пошуку в масиві і зійшлися на одному елементі (L = R). Чому саме перевіряється на збігання елемент масиву з індексом R, а не L? Проаналізуємо оператор розгалуження, який використовується для знаходження певного елемента:
if a[m]<x
then L := m + 1 else R := m;
Якщо шуканого елемента в масиві немає, то ми весь час від-кидаємо ліву половину масиву, тим самим просуваючи значения L до R. При цьому значения R не змінюється, тобто зали-шається R = N. Якщо ж шуканий елемент у масиві присутній, то найімовірніше, що протягом виконання алгоритму відбу-валося відкидання як правої, так і лівоїчастин. Тому перевірка на наявність шуканого елемента в масиві зводиться до пере-вірки умови, чи є елемент, на якому ми зійшлися, шуканим: a[R] = х. Якщо ж ця умова не виконується, то це означатиме, що шуканого елемента в масиві немає. Особливим випадком є ситуація, коли шуканий елемент знаходиться на N-му місці в масиві. Але і при цьому знайдемо правильну відповідь.
Оцінкою ефективності виконання алгоритму бінарного пошуку є значения 0(log2N), оскільки саме стільки порівнянь буде виконано в запропонованому алгоритмі. Детально це питания розглядалося при ознайомленні з визначенням оцінки ефек-тивності роботи розроблених алгоритмів у першому розділі.
113
/ Запитання для самоконтролю
-
Навєдіть алгоритм пошуку необхідної інформації у словнику.
-
Сформулюйте задачу пошуку заданого елемента в упорядкованому масиві.
-
Запишіть алгоритм бінарного пошуку.
-
Як визначається «центральний» елемент масиву, з яким відбу-вається порівняння при бінарному пошуку?
-
Як відбувається «відкидання» правоі та лівої частин масиву при бінарному пошуку?
-
Запишіть алгоритм і текст Pascal-програми бінарного пошуку інформації, при виконанні якого використовується логічна змінна як ознака завершения алгоритму.
-
Як можна вдосконалити алгоритм, наведений у попередньому пункті? Обґрунтуйте свою відповідь.
-
Запишіть найефективніший варіант алгоритму бінарного пошуку. Обґрунтуйте ефективність окремих фрагментів цього алгоритму.
-
Проаналізуйте умову завершеності алгоритму, наведеного в попередньому пункті.
10. Якою є оцінка ефективності роботи бінарного пошукового алгоритму?
Завдання
-
Реалізувати алгоритм бінарного пошуку заданого елемента в упорядкованому масиві, перевіряючи його на збігання з елементом, що мае порядковий номер т мовою Pascal. Визна-чити кількість порівнянь, необхідних для виконання цього алгоритму.
-
Реалізувати алгоритм бінарного пошуку заданого елемента в упорядкованому масиві, не перевіряючи його на збігання з елементом, що мае порядковий номер т мовою Pascal.
-
Порівняти виконання алгоритмів щодо кількості викона-них порівнянь у завданнях 1,2, протестувавши їх для випад-ків, коли шуканий елемент розміщений:
-
на початку масиву;
-
у кінці масиву;
-
посередині масиву.
4. Зробити письмовий аналіз завдання 3.
РЕКУРСИВШ ПОШУКОВІ АЛГОРИТМИ