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

Метод прямого злиття

Почнемо з відповіді, на перший погляд, на дуже просте запитання: який за довжиною масив можна вважати стовід-сотково відсортованим? Відповідь однозначна: масив, який складається з одного елемента.

Справді, такий масив стовідсотково є одночасно впорядко-ваним як за зростанням, так і за спаданиям. А як із двох упо-рядкованих масивів, що складаються з одного елемента кож-ний - at i bj, зробити один упорядкований масив ct, i = 1,2? Відповідь однозначна: якщо аг < bv то сл = av с2 - bv у про-тилежному випадку ct= bv сг = av Таким чином злилися дві впорядковані послідовності, що містили по одному елементу, в упорядковану послідовність з двох елементів.

Чи можна продовжити далі міркування щодо злиття двох упо-рядкованих послідовностей, що містять по два елементи, в одну впорядковану послідовність, що складатиметься з чотирьох елементів? Цей алгоритм буде вже складнішим, ніж поперед-ній, але все одно досить прозорим. Як ми діяли б інтуїтивно?

183

Будемо міркувати так. Якщо а, < Ь,, то с1 - av інакше 1 = bv Наступний вибір для перенесения значения елемента в с2 буде відбуватись між аг та Ь1 у першому випадку та між а1 та Ьг - у другому. Як не заплутатися в тому, який елемент з масиву а та масиву b записаний на даному кроці в масив с? На якому еле­мента в кожному з масивів ми зупинилися? У даному випадку найпростіше навести схему алгоритму злиття двох упорядкова-них послідовностей, у кожній із яких по два елементи, в одну упорядковану послідовність із чотирьох елементів (мал. 79).

Мал. 79

Тепер уже вимальовується алгоритм для злиття впорядкова-них послідовностей із чотирьох елементів у впорядковану послідовність із восьми елементів. Але запис такого алгоритму стае вже надто заплутаним. Варто знайти якусь закономірність.

Для розв'язання даної ситуації треба ввести змінні, які бу-дуть поточними індексами для роботи з кожним із цих масивів. Отже, для масиву а визначимо індекс і, для масиву Ь - індексу', а для масиву с - індекс k.

n:=4;

i:=1;j:=1;k:=1; while (i <= n) and (j <= n) do begin

if a[i]<b[j] then begin

elk] := a[i]; inc(i); inc(k) end

else begin

с[к]:=ЬШ; inc(j); inc(k) end; end; while i <= n do {Дописування у кінець масиву с залишків масиву а.}

begin

c[k]:=a[i]; inc(i); inc(k) end; while j <= n do {Дописування у кінець масиву с залишків масиву Ь.)

begin

c[k] := ьш; lnc(j); inc(k) end;

Тепер цей алгоритм можна застосувати для злиття будь-яких двох однакових за кількістю елементів упорядкованих масивів в один, також упорядкований. У новому злитому ма-сиві буде 2га елементів.

Але насправді маємо зовсім іншу ситуацію. У нас немає двох масивів, які треба злити в один, а є один масив, який треба упо-рядкувати.

А для чого ж ми стільки розбиралися з попереднім алго­ритмом? Застосуемо його наступним чином. Розбиватимемо наш масив на частини, які зливатимемо разом. Злиті частини можна записувати в новий масив, а потім переписувати його на «свое» місце в заданий.

Але й цю процедуру можна спростити: продовжити заданий масив удвічі, тобто наперед зарезервувати для нього місце на 2га елементів. Тепер після першого злиття результат буде міс-титися у частині а„ + 1, ••-, а2п, і ЇЇ можна зробити вхідним ма-сивом для наступного кроку злиття. Результат другого кроку злиття будемо писати в першу половину масиву ах, ..., ап. Та­ким чином, на кожному кроці злиття заданого масиву дов-жиною в га елементів будемо робити послідовні кроки по пе-ремиканню злиття першої половини масиву ау, ..., а2п у другу, і навпаки.

Контроль за тим, яка з половин масиву а,, ..., а є вхідною, а яка вихідною, здійснюватиме логічна змінна up. Коли вона набуватиме значения true, то це означатиме, що вхідним є пер­ша половина масиву а:, ..., a2n, a вихідним - друга, коли false, то навпаки. При цьому на кожному такому кроці збільшувати-мемо розміри фрагментів масиву, що зливаються, за прин­ципом р := р * 2.

Але й це ще не все. Рухатися вздовж масиву, наприклад, а,, ..., ал, міняючи індекси з крокомр, - зайві ігри. Оскільки

185

маємо справу зі структурою прямого доступу, то час доступу до будь-якого елемента однаковий. Давайте зробимо так, щоб лег-ше було скласти алгоритм. Уведемо таку індексацію: iraj- ін-декси елементів, що сортуються, для ситуації, коли злиття від-бувається у першій половині масиву, k та I - відповідно для другої половини масиву. Тому слід зміну індексу і починати з 1, a kз п + 1, зміну індексу у — з останнього елемента п, a Iз 2п. А далі під час злиття і та k збільшуватимемо, a ;' та I - зменшу-ватимемо, поки вони не зійдуться посередині.

Для зручності роботи з індексами писати результат злиття будемо так само: у початок вихідної частини масиву, потім у кі-нець, і т. д., сходячись до центра.

Розглянемо описаний алгоритм покроково на прикладі (мал. 80).

;

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

44

55

12

42

94

18

06

67

!

2

л

4

5

в

7

S

9

10

11

12

13

14

15

16

44

55

12

42

94

18

06

67

44

67

55

06

06

55

/

2

3

4

5

е

7

8

9

10

11 |12\ 13

14

15

16

44

55

12

42

94

18

06

67

44

67

12

55

06

'Ir-r-J'

12

18

k

I Ы 19 лам ;

1

2

3

4

5

6

7

8

9

10

Л

1:: ! /.:

/4

І5 1 26

44

55

12

42

94

18

06

67

44

67

12

18 |94

42

55

06

/*т*Ч

186

Мал. 80

Схематично описаний процес злиття заданого масиву можна зобразити так, як показано на малюнку 81.

вхіднии масив

вихіднии масив

Мал. 81

А тепер перейдемо до кодування алгоритму прямого злиття MergeSort у вигляді тексту Pascal-програми. Запишемо спочат-ку процедуру злиття в загальному вигляді:

procedure MergeSort; var i, j, k, I: integer;

up: boolean; p: integer; begin

{Визначення індексів.}

up :=true; p := 1; repeat if up then begin

| i:=1;j:=n;k:=n + 1;l:=2*n end else begin

| k:=1;l:=n;i:=n+1;j:=2*n end; <Алгоритм злиття р-наборів з / тау'-входів у к та /-виходи.> up := not (up); p := 2 * p; until p = n; end;

Уточнимо ту частину алгоритму, яка безпосередньо виконує процес злиття та працюе для будь-якого п, увівши ще й такі змінні: т - кількість елементів, які ще залишилося обробити; q та г - довжини підпослідовностей, що зливаються.

Оскільки п не завжди є степенем 2, то умовою виходу буде р > п. 3 цієї причини підпослідовності з q та г елементів не завжди зійдуться в центрі. Тому залишки копіюватимемо у кінець вихідної послідовності.

Отже, уточнений і повний алгоритм прямого злиття у вигля-ді тексту Pascal-програми такий:

procedure StraightMerge; var i, j, k, I, t: integer; h, m, p, q, r: integer;

8*

187

up: boolean; begin

up := true; repeat

h:= 1; m :=m; if up then begin

| i := 1; j := n; к := n + 1; I := 2 * n end else begin

| k:= 1;l:=n;i:=n + 1;j:=2*n end; repeat {Злиття серій з /' та/'-входів у /с-вихід.)

if m > = р then q := p else q := m; m := m - q;

if m > = r then r := p else r := m; m :=m- r;

while (q <> 0) and (r <> 0) do {Злиттядтагелементів.}

ifa[i]<a[j] then begin

a[k]:=a[i];k:=k + h; i := i + 1; q := q - 1 end else begin

a[k] :=a[j];k:=k + h; j:=j-1;r:=r-1 end; while r > 0 do begin

a[k]:=a[j];k:=k + h; j := j - 1; r := r- 1 end; while q > 0 do begin

a[k]:=a[i];k:=k + h; i :=i+ 1; q :=q - 1 end;

{Перемикання на кінець вихідного файла) h := - h; t := k; k := I; I := t; {для рівномірного розподілу.)

until m = 0;

up := not up; p := 2 * p; until p > ■ n; if not up then for i := 1 to n do a[i] :=a[i + n]; end;

188

Описаний алгоритм був запропонований Н. Віртом, і саме тому ми надали йому перевагу. Але виникає слушне запитан-ня: а чи можна його спростити? Так, звичайно. Можна знехту-вати перемиканням вхідної та вихідної частил подвійного за дЪвжиною масиву. Для цього ввести ще один масив такої самої довжини, як і заданий. Злиття робити у цей додатковий масив, переписуючи згодом упорядковані частини з нього в заданий масив.

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

Спочатку розглянемо процедуру, що реалізує алгоритм двох сусідніх серій в одну:

procedure merger (var one, two: mas); var q, x, y, en: word; begin x:=i; у :=i + k; if i + 2 * k - 1 > n then en := n else en := i + 2 * k — 1; for q := i to en do

if (x <= i + k - 1) and (y <= en) then

if (one[x]<one[y])

then begin two[q] := one[x]; inc (x) end else begin two[q] := one[y]; inc (y) end else

if x>i + k-1

then begin two[q] := one[y]; inc (y) end else begin two[q] := one[x]; inc (x) end; end;

Основна частина алгоритму передбачає поділ масиву на серії довжиною 2*~\ для k = 1, 2, ..., та наступив їх злиття. Робота відбувається почергово з масивами а та Ь: з одного масиву ін-формація береться як із вхідного, а в другий записуеться як у вихідний. На наступному проході масиву з подвоєною кіль-кістю елементів серїї масиви міняються місцями за призна-ченням:

flag := true; k:=1;

while k<n do begin

i:=1;

whilei < n- k+ 1 do

189

begin

if flag then merger (a, b) else merger (b, a); inc (i, 2 * k); end; if i <= n then if flag

then for j := i to n do b[j] := a[j] else for j :-itondoa[j] :=b[j]; k:=k*2; flag := not flag end;

Оцінимо ефективність роботи алгоритму прямого злиття. Маємо справу з подвоєнням розмірів частин масиву, які зли-ваються. Раніше (у методах швидкого та пірамідального сорту-вання) ми, навпаки, поділили масив навпіл, переміщалися бі-нарним деревом у попіуках елементів для обміну. Але від цього кількість кроків, необхідних для впорядкування масиву, не змінюється. Однак за умовою алгоритму сортування припи-няється, коли р > п, і залишок елементів, кількість яких мен-ша зар, просто переписуємо у вихідний масив. Тому насправді для методу прямого злиття необхідно [log2n] проходів масиву. I загальною оцінкою даного методу є 0(п [log2n]).

Оскільки метод прямого злиття працює не для всіх типів змінних і розмірностей масивів, то при його тестуванні треба збалансувати ці два фактори для п > 100. Вхідні дані, на яких рекомендується протестувати розроблений алгоритм, тра-диційно повинні бути такими:

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

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

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

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

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

  1. Для яких сукупностей елєментів були свого часу запропоновані методи сортування? Як називаються такі сукупності елементів?

  2. Яка технічна проблема спричинила розробку цих методів сорту­вання?

  3. У чому полягає ідея методу прямого злиття для сортування еле-мент'ш?

  4. Наведіть власний приклад сортування восьми елементів ме­тодом прямого злиття. Проілюструйте алгоритм методу прямо­го злиття на наведеному прикладі.

  5. Запишіть алгоритм злиття в упорядковану послідовність двох масивів з п елементів кожний.

  6. Як можна модифікувати алгоритм, наведений у запитанні 5, для впорядкування елементів заданого одновимірного масиву?

190

  1. Схематично зобразіть роботу алгоритму, наведеного у запитан-ні 6, на конкретному прикладі.

  2. Запишіть алгоритм і текст Pascal-процедури MergeSort, що реалізує метод прямого злиття для сортування елементів одно-вимірного масиву.

  3. Який ще варіант реалізації алгоритму сортування елементів ма­сиву методом прямого злиття можна запропонувати? Обґрун-туйте його та запишіть фрагмент алгоритму і текст Pascal-npo-грами.

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

Завдання

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

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

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

  4. Проаналізувати покрокове виконання завдання 3 щодо кількості виконуваних дій.

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

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

Соседние файлы в папке Методи побудови алгоритмів та їх аналіз