Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
О.О.П / ооп / 4_кол / К курсовой / Методи побудови алгоритмів та їх аналіз. Караванова Т.П. / Інформатика_1 (методи побудови алгоритмівта та їх аналіз).doc
Скачиваний:
89
Добавлен:
30.05.2020
Размер:
2.5 Mб
Скачать

Швидке сортування

Метод швидкого сортування Quicksort є удосконаленням «бульбашкового» сортування, або методу прямого обміну. Ідея його була запропонована Чарльзом Хоаром у 1962 р. і полягає в тому, що краще спочатку здійснювати обміни на великих від-станях, які потім зменшуються.

Спробуємо дійти до ідеї алгоритму поступово. Спочатку роз-глянемо вже відсортований за спаданиям масив

94, 67, 55, 44, 42, 18, 12, 06.

Неважко помітити, що якщо у відсортованому за спаданиям масиві взяти будь-який елемент а,, де 1 < i < п, то всі елементи

176

зліва від нього будуть більші або рівні за даний, а справа - мен-mi або рівні.

I ще одна цікава закономірність: масив можна відсортувати за спаданиям за [п/2] кроків, помінявши спочатку місцями пер­ший і останній елементи, потім другий і передостанній і т. д.

Скористаємося цими ідеями для довільного масиву а., а2,..., ап. Візьмемо будь-який елемент масиву ак (1 < k < п). Нехай його значениям є х. Такий елемент носить назву осьового, він умов-но ділить весь масив на дві частини. Тепер будемо послідовно зліва шукати елемент а( > х, а справа а.< х. Знайшовши такі елементи, поміняємо їх місцями, оскільки у впорядкованому варіанті даного масиву вони мають розміщуватися саме так. Продовжимо цей процес доти, поки / та j не зустрінуться. У ре­зультат! отримаємо масив, у якому всі елементи будуть поділені на дві частини відносно позиції і = j масиву: зліва від цієї позиції будуть усі елементи, менші за х, а справа - більші.

Використання ідеї' поділу масиву на дві частини при побу-дові алгоритму сортування відносить його до алгоритмів, побу-дованих на принципі «розділяй і володарюй».

Розглянемо приклад уже відомого нам масиву (мал. 71).

44, 55, 12, 42, 94, 06, 18, 67.

Мал. 71

У якості осьового елемента х візьмемо елемент зі значен­иям 42. У результаті двох обмінів (18 ** 44) і (55 ** 06) отри-маемо результат, зображений на малюнку 72.

;

2

3

4

5

6

7

8

/

2

3

4

5

6

7

S

44

55

12

42

94

06

18

67

18

55

12

42

94

06

4 1

67

t t

i 3 a)

t І

6)

T. j

1

2

3

4

5

6

7

8

1

2

3

4

5

6

7

8

18

06

12

42

94

55

44

67

18

06

12

42

94

55

44

67

Tt

i j

t

j

t І

r)

в)

Мал. 72

Фрагмент програми, який відповідає запропонованому алго­ритму, можна записати так:

177

i:= 1;j :=n; repeat

while a[i]<x do i := i + 1; while x<a[j] doj :=j- 1; if i <= j then begin

| c:=a[i];a[i] :=a[j];a[j] :=c end; i:=i+1;j:=j- 1; until i > j;

Якщо виконати цей алгоритм за малюнком 72, то виявить-ся, що малюнок 72, в не досить точно відтворює положения ін-дексів і та у по завершению циклу. Виправимо цю неточність (мал. 72, г).

Пояснимо малюнок 72, г: після останнього обміну 55 *-» 06 результатом виконання циклу while a[i] < х do i := i + 1 буде i - 4, a, = 42, а циклу while x < a[j] do j := j - 1 буде j = 4, ay = 42. Фраг­мент алгоритму, який виконується після завершения обох циклів, доводить, що значения параметрів і та ; змінюються: і := і + 1; j :s j - 1;. Отже, по завершению цього алгоритму і = 5, а j = 3, тобто їх стан такий, як зображено на малюнку 72, г.

Для того щоб відсортувати весь масив, залишилося цю так­тику застосувати до лівої (1 < k < 3) і правої (5 < k < 8) частин отриманого поділу, потім до лівої та правої частин правої час-тини та відповідно до лівої та правої частин лівої частини пер-шого поділу і т. д. Цей процес будемо продовжувати доти, поки кожна частина не буде складатися з одного елемента.

Продовжимо процес, зображений на малюнку 72, представ-ляючи описаний алгоритм двома фазами сортування масиву на кожному кроці поділу підмасиву на дві частини - ліву і праву -таким чином (мал. 73-76). На лівому малюнку виділятимемо темно-сірим кольором ту частину масиву, яку опрацьовувати-мемо за нашим алгоритмом, виділяючи напівжирним шриф­том осьовий елемент, а світло-сірим - елементи, які уже стоять на своїх місцях. На правому малюнку зображатимемо цю час-тину масиву після обміну місцями лівих і правих елементів ви-діленої частини відносно осьового елемента.

х-т

1

2

3

4

5

6

7

8

18

06

12

42

94

55

44

67

/

2

3

4

5

6

7

8

00

18

12

42

94

55

44

67

т т т т

і і j і

а) б)

Мал. 73

178

х = 18

1

2

3

4

5

6

7

8

i

2

3

4

5

б

7

8

06

18

12

42

94

55

44

67

06

12

18

42

94

55

44

67

Т Т

т т

і І

б)

а)

Мал. 74

х = 55

І

2

3

4

5

6

7

8

/

2

3

4

5

6

7

8

06

12

18

42

94

55

44

67

06

12

18

42

44

55

94

67

а)

I У

б)

Мал. 75

ж = 94

1

2

3

■/

5

б

7

8

1

1'

3

4

5

6

7

8

06

12

18

42

■11

55

94

67

06

12

18

42

44

55

67

94

т т

Т Т і І

а)

б)

Мал. 76

Проблема полягає лише у виборі значения х. Найчастіше в якості х обирають середній за розміщенням у масиві елемент, як це зображено на малюнках 73-76.

ПДкавим є випадок, коли в якості осьового елемента є наймен-ший або найбільший елемент даного фрагмента. Таку ситуацію можна спостерігати на малюнку 73, а. Однак це не є перешкодою. Розглянемо її детальніше. Осьовим елементом е х = а2- 06, гра-ницями фрагмента масиву, який упорядковується, і = 1, / = 3. Згідно з алгоритмом цикл while a[i] < х do i := i + 1 (a1> x) за­вершиться значениям i = 1, а цикл while x < a[j] do j := j - 1 - зна­чениям j = 2 (x < a3, x ■ a2). Подальше виконання алгоритму при i <= j = true призведе до обміну значень а1 = 18 ** а2 ■= 06, тобто елементи масиву набудуть значень а1 = 06 і аг - 18. Далі за ал­горитмом буде збільшено значения і та зменшено значения j: і - 2, / -= 1. Ці нові значения лівої і правої меж підмасиву, який сортується, дають змогу завершити цикл > j •= true). Таким чином, пересвідчилися, що навіть у випадку, коли осьовий еле­мент х є найменшим або найбільшим елементом фрагмента ма-

179

сиву, який сортується, це не є перешкодою для коректного сорту-вання.

Реалізація запропонованого алгоритму буде мати рекурсив­ный вигляд. А саме фрагмент алгоритму, який виконує обмін елементів масиву відносно граничного елемента х, оформимо у вигляді рекурсивно! процедуры sort.

procedure sort (L, R: integer); var i, j: integer; c, x: real; begin

i:=L;j:=R; x:=a[(L + R)div2]; repeat while a[i]<x do i := i + 1; while x<afj] do j :=j-1; if i<=jthen begin

с:=а[і];а[і]:=аШ;аШ:=с; i:=i + 1;j:=j-1; end; until i > j;

if j > L then sort (L, j); if i<R then sort (i, R) end;

Виклик цієї рекурсивної процедури в основній програмі можна здійснити так:

sort (1, n);.

Для визначення оцінки ефективності роботи методу швид-кого сортування ще раз нагадаємо описаний вище алгоритм.

Оскільки ми кожного разу поділяли масив на дві частини, а отримані дві частини знову на дві, то це приводить до думки про існування логарифмічної залежності, яка була визначена і під час аналізу алгоритму пірамідального сортування. «Гли-бина» такого поділу, або кількість поділів, буде log2n. Можна представити процес поділу масиву навпіл у вигляді дерева ре-курсії на схемі (мал. 77).

3 малюнка 77 видно, що кількість поділів дорівнює log2n, а кількість елементів на кожному рівні поділу становить п еле-ментів. Тому результуюча оцінка методу швидкого сортування становить n\og2n.

Ми розглянули алгоритм швидкого сортування для випад-ку, коли на кожному кроці рекурсії поточний масив ділиться практично навпіл. Це і дає схему, наведену на малюнку 77, та логарифмічну оцінку ефективності такого методу.

180

п/4 п/4 п/4 п/4

/\ /\ /\ /\

log2n п/8 п/8 п/8 п/8 п/8 п/8 п/8 п/8

*- п


> п

--*■ п

11111111111111111-*/»

Мал. 77

Однак саме коректність поділу є основою такої оцінки. Роз-

глянемо варіант іншого поділу масиву: у пропорції . Тоді

л-1 схема поділу масиву на частини матиме такий вигляд (мал. 78):

/"\ """""'"

1 ,л-1 -»• п

/ \

1 ,п-2 - -► л-1

/ \

п

1 ,п-3 > л-2

/ ч

1

3

2

1 1

Мал. 78

3 дерева рекурсії, зображеного на малюнку 77, можна зроби-ти висновок, що оцінка методу швидкого сортування при тако­му алгоритмі поділу масиву на частини складає 0(п2). Тобто цей поділ є чи не найгіршим. Отже, висновок мае бути таким: від принципу поділу масиву на дві частини залежить ефектив-ність роботи алгоритму швидкого сортування. I найкращим по-ділом є поділ навпіл, якого треба прагнути, використовуючи метод швидкого сортування.

Швидке сортування вважається одним із найкращих методів. При тестуванні цього алгоритму необхідно для п = 10, п = 100, п = 1000, п = 10 000, п = 30 000 згенерувати такі вхідні дані:

181

  • вхідний масив уже впорядкований необхідним чином;

  • вхідний масив є частково впорядкованим;

  • вхідний масив упорядкований у зворотному порядку;

  • елементи вхідного масиву розміщені випадковим чином.

/ Запитання для самоконтролю

  1. На якому з методів прямого сортування базується метод швид-кого сортування?

  2. У чому полягає ідея алгоритму швидкого сортування?

  3. Який елемент може відігравати роль осьового елемента і яке йо-го призначення в методі швидкого сортування?

  4. Продемонструйте роботу методу швидкого сортування на влас-ному прикладі одновимірного масиву.

  5. Запишіть фрагмент алгоритму у вигляді тексту Pascal-програми, який реалізує обмін елементів масиву відносно визначеного осьового елемента.

  6. Продемонструйте роботу алгоритму, наведеного в запитанні 5, на конкретному прикладі, обґрунтовуючи переміщення індексів / та/.

  7. Вибір якого з елементів масиву в якості осьового є найефектив-нішим?

  8. Продемонструйте роботу алгоритму, наведеного в запитанні 5, на власному прикладі у випадку, коли осьовий елемент є най-більшим або найменшим у поточному фрагменті масиву, що впорядковуеться.

  9. Як виглядатиме рекурсивний алгоритм методу швидкого сорту­вання мовою Pascal?

  1. Як здійснити виклик рекурсивної процедури, що реалізує алго­ритм методу швидкого сортування, з тіла основної програми?

  2. Якою є оцінка ефективності роботи алгоритму швидкого сорту­вання? Обґрунтуйте свою відповідь.

Завдання

  1. Реалізувати у вигляді програми алгоритм швидкого сор­тування заданої послідовності за зростанням.

  2. Модифікувати алгоритм, представлений у завданні 1, так, щоб сортування відбувалося за спаданиям.

  3. Протестувати реалізовані у завданнях 1-2 алгоритми для масиву 15, 4, 10, 8, 6, 9, 16, 1, 7, 3, 11, 14, 2, 5, 12, 13.

  4. Проаналізувати покрокове виконання завдання 3 для впо-рядкування заданої вхідної інформації за зростанням, контро-люючи зміну значень змінних i, j та х.

  5. Шдібрати власні тести, які доводять переваги й показу-ють недоліки методу швидкого сортування при п > 100.

  6. Зробити письмовий аналіз завдань 3-5.

182

СОРТУВАННЯ ПОСЛІДОВНОСТЕЙ

Досі розглядалися сукупності елементів, кількість яких відповідала розміру пам'яті комп'ютера. Але інколи виникає потреба працювати із сукупностями, які неможливо заван-тажувати в пам'ять комп'ютера у масиви. Такі сукупності знаходяться у файлах на зовнішніх носіях. Але оскільки для реалізації подальших алгоритмів потрібний прямий доступ до елементів цих файлів, то зрозуміло, що і файли ці повинні бути прямого доступу. Такими можуть бути в Pascal типізовані файли. Означену сукупність елементів, записаних у файли прямого доступу, будемо називати послідовністю.

Наступні два методи були описані Н. Віртом саме для сорту-вання даних, які з-за свого розміру не можуть бути розміщені в пам'яті комп'ютера. Однак сьогоднішні технічні можливості комп'ютерів знімають саме ці фактори. Тому переваги методів сортування послідовностей, зазначені свого часу Н. Віртом, на сьогодні вже втратили свою актуальність.

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

Надалі розглянемо методи злиття послідовностей на при­кладах масивів.