You are on page 1of 15

Міністерство освіти і науки України

Вінницький національний технічний університет


Факультет інтелектуальних інформаційних технологій та автоматизації
Кафедра КН

Лабораторна робота №7
з дисципліни «Теорія алгоритмів»

Виконала: ст. гр. 1КН-22б. Пяста Марія


Перевірив: Арсенюк І.Р.

Вінниця 2023
Лабораторна робота №7
Тема: програмування та аналіз алгоритму сортування за допомогою купи
(пірамідальне сортування). Обчислення часу виконання алгоритму.
Мета: проаналізувати та дослідити алгоритм сортування методом купи.

Порядок виконання роботи

1. Тему, мету та порядок виконання роботи.


2. Ідея відповідного алгоритму сортування.
3. Власний приклад роботи відповідного алгоритму сортування на
масиві 10 чисел
4. Алгоритм сортування у графічному вигляді.
5. Теоретична оцінка складності алгоритму для трьох випадків.
5.1 для найкращого випадку. 5.2 для найгіршого випадку. 5.3 для
середнього випадку.
6. Сирець алгоритму відповідного сортування.
7. Практична оцінка складності відповідного алгоритму в трьох
випадках для масивів різного розміру. Навести таблиці та відповідні
графіки часу виконання програми для трьох випадків. Кількість
значень часу має бути не менше семи.
7.1 Таблиця та графік для найкращого випадку.
7.2 Таблиця та графік для найгіршого випадку.
7.3 Таблиця та графік для середнього випадку.
8. Порівняльний аналіз теоретично та практично отриманих графіків
залежностей часів виконання програми від розмірів входу.
9. Переваги та недоліки дослідженого Вами алгоритму сортування та
Ваші міркування.
10. Розширені висновки з роботи.

1
Хід роботи

1. Ідея відповідного алгоритму сортування

Сортування купою(англ.Heapsort)-алгоритм сортування, в основі якого


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

Алгоритм сортування з використанням купи (пірамідальне сортування)


використовує структуру даних – двійкову купу. Двійкова купа – це масив із
визначенимивластивостями впорядкованості.

Основні операції над купою

• Процедура HEAPIFY дозволяє підтримувати головні властивості купи.


Час її роботи 𝜃(𝑙𝑜𝑔𝑛)
• Процедура BUILD-HEAP будує купу з вхідного (невідсортованого) масиву.
Час її роботи = 𝜃(𝑛)
• Процедура HEAPSORT сортує масив без використання допоміжної пам’яті.
Час її роботи 𝜃(𝑛𝑙𝑜𝑔𝑛)
• Процедура EXTRACT-MAX (отримання найбільшого) і INSERT
(доповнення елементу) використовується під час моделювання черги
з пріоритетами на базі купи.
Час роботи обох процедур = 𝜃(𝑙𝑜𝑔𝑛)
Даний алгоритм має 2 частини:
1) Викликається процедура BUILD-HEAP і масив стає купою.
2) Ідея другої частини така: максимальний елемент масиву тепер знаходиться
у корені дерева А[1]. Його слід поміняти з елементом А[n], зменшити розмір
купи на 1 і відновити головну властивість у кореневій вершині (оскільки
піддерева з коренями LEFT(1) і RIGHT(1) не втратили головної властивості
купи, це можна зробити за допомогою процедури HEAPIFY. Після цього у
корені знаходитиметься мах. з елементів, що залишився. Так робиться
допоки в купі не залишиться 1 елемент.
Елементи, що зберігаються в купі повинні володіти головною властивістю
купи: для кожної вершини i, крім кореня (при 2≤i≤heap-size[A]}:
А[PARENT(i)] ≥ A(i) (*)
Отже, значення нащадка НЕ ПЕРЕВИЩУЄ значення батька.

2
Таким чином, найбільший елемент дерева (або будь-якого піддерева)
знаходиться у кореневій вершині цього дерева (або піддерева)
Висота вершини дерева – це число ребер у найдовшому шляху, який
починається у цій вершині, йде вниз по дереву
і закінчується листком. Тобто, висота дерева збігається з висотою його
кореня.
• У дереві, що утворює купу, всі рівні (крім останнього)
заповнені повністю.
Тому висота цього дерева дорівнює O(logn), де n – число елементів купи.
• Час роботи основних операцій над купою пропорційний висоті дерева і,
відповідно,
складає O(logn).

2. Власний приклад роботи відповідного алгоритму на масиві з 7


чисел

Крок 1

Починаючи з нульового елементу послідовно заповнюємо рівні дерева


зліва направо, як показано на малюнку нижче. Виходить, що корень дерева
— це нульовий елемент масиву, його нащадки — це перший і другий
елемент і, відповідно, нащадки лівої гілки — це 3 і 4 елемент, а правої — 5
і 6 і так далі. (рис.1)

3
Рис.1.1

Крок 2

Відтак, нам потрібно перетворити це дерево таким чином, щоб у корені


дерева знаходився максимальний елемент, а значення у кожному вузлі
було більшим, ніж значення в його нащадках. Для цього ми дивимся на
передостанній рівень дерева і для кожного вузла перевіряємо, чи
виконується умова: кожен нащадок має бути менший за свого батька.
Якщо ні, то знаходимо максимальний елемент серед нащадків і міняємо
його місцями з батьківським. Проходимо таким чином кожним рівнем
дерева знизу вгору і в результаті ми отримаємо сортувальне дерево, в
корені якого знаходиться максимальне число. Перетворене дерево
виглядатиме ось так: (рис.1.2)
Всі перестановки, які ми нібито робимо в дереві — це переставляння
елементів у масиві. Відповідність індексів масиву і вузлів було вказано на
першому малюнку. По суті, ми поміняли місцями 1 <—> 4 елемент масиву
і 2 <—> 6.
Масив на цьому кроці виглядає [9, 8, 4, 7, 3, 1, 2].

Рис.1.2

4
Крок 3

Тепер ми поміняємо місцями нульовий і останній елемент масиву і


«відріжемо» гілку з максимальним елементом. Масив тепер виглядає так:
[2, 8, 4, 7, 3, 1, 9].

Рис.1.3

Крок 4

Все, що потрібно робити далі — це схожим чином привести дерево до


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

5
Рис.1.4

Знову у нас максимальний елемент в корені, і ми міняємо його місцями з


передостаннім елементом масиву, так як останній елемент і так є
максимальним серед усіх. «Відрізаємо» від дерева гілку з максимальним
елементом. Масив матиме наступний вигляд: [1, 7, 4, 2, 3, 8, 9].
Таким чином, в кінці масиву ми складаємо вже відсортовані елементи.
Ітерації виконуються до тих пір, поки масив не буде повністю
відсортовано, і все листя з дерева не буде зрізане.

6
3. Алгоритм сортування у графічному вигляді.

4. Теоретична оцінка складності алгоритму для трьох випадків.

Пірамідальний алгоритм відрізняється чудовою обчислювальною


складністю і не потребує додаткової пам’яті.
Сортування купою — алгоритм сортування, який працює в гіршому, в
середньому і в найкращому випадках (тобто гарантовано)
за Θ(n log n) операцій при сортуванні n елементів. Кількість
застосовуваної службової пам'яті не залежить від розміру масиву (тобто
потребує додаткову пам’ять O (1), а не О(n),як сортування злиттям.

Алгоритм пірамідального сортування складається з двох кроків. На


першому кроці, для вхідного масиву даних здійснюється відновлення
структури двійкової купи. Відновлення полягає у тому, що для всіх
внутрішніх вузлів дерева двійкової купи здійснюється просіювання вниз.
При цьому, у випадку, якщо вхідний масив необхідно відсортувати за
зростанням (не спаданням) елементів, то просіювання здійснюється так,
щоб найбільший елемент структури знаходився у корені дерева. У

Другий крок алгоритму сортування полягає у тому, що найбільший


елемент двійкової купи міняється місцями з останнім елементом масиву –
який займає свою позицію у відсортованому масиві. Після цього, для
елемента, що опинився у вершині піраміди здійснюється просівання вниз
(не зачіпаючи елемент масиву, що опинився на останній позиції). Далі
міняємо місцями елемент у вершині з передостаннім елементом масиву та
здійснюємо просіювання вниз. Цю процедуру здійснюємо доти, доки всі
елементи не займуть свої позиції у відсортованому масиві.

7
5. Практична оцінка складності відповідного алгоритму в трьох
випадках для масивів різного розміру. Навести таблиці та відповідні
графіки часу виконання програми для трьох випадків. Кількість значень
часу має бути не менше семи. Увага! Мінімальне та максимальне
значення часу кожним студентом підбирається індивідуально, залежно
від потужностей Вашого ПК. При цьому врахуйте, що мінімальне
значення часу повинно
бути порядка кількох секунд (до 10), а максимальне – не більше 5 – 10
хвилин (за Вашим бажанням останній час може бути збільшено). 7.1
Таблиця та графік для найкращого випадку. 7.2 Таблиця та графік для
найгіршого випадку. 7.3 Таблиця та графік для середнього випадку.

Середній випадок

n t, с
10000 0.034052
50000 0.196202
100000 0.41826
200000 0.892817
300000 1.404840
400000 1.915475
500000 2.491182
600000 3.127492
800000 4.31716
1000000 5.499669

Найкращий випадок

n t, с
10000 0.031322
50000 0.191432
100000 0.413479
200000 0.886670
300000 1.395457

8
400000 1.891927
500000 2.488069
600000 3.064783
800000 4.219747
1000000 5.337023

Найгірший випадок

n t, с
10000 0.039718
50000 0.200475
100000 0.427104
200000 0.918864
300000 1.423789
400000 1.936715
500000 2.49664
600000 3.140472
800000 4.373874
1000000 5.506510
t,

n, кількість елементів

Рис.1.1 графік практичної складності алгоритму методом купи для 3


випадків

9
Лістинг програми для оцінки часової складності алгоритму методом купи
для 3 випадків :

import
random
import time

def heapify(arr, n, i):


largest = i
left = 2 * i
+1
right = 2 * i + 2

if left < n and arr[left] >


arr[largest]: largest = left

if right < n and arr[right] >


arr[largest]: largest = right

if largest != i:
arr[i], arr[largest] = arr[largest],
arr[i] heapify(arr, n, largest)

def heapSort(arr):
n = len(arr)

# Build a max heap


for i in range(n // 2 - 1, -1, -
1): heapify(arr, n, i)

# Extract elements from the heap one by


one for i in range(n - 1, 0, -1):
arr[i], arr[0] = arr[0],
arr[i] heapify(arr, i, 0)

10
# Generate an array of numbers
size = 700000 # Specify the size of the array
numbers = [random.randint(1, 700000 ) for _ in range(size)]

# Worst Case
worst_case_numbers = list(reversed(numbers)) # Sorted in descending order

# Best Case
best_case_numbers = sorted(numbers) # Already sorted in ascending order

# Average Case
average_case_numbers = list(numbers) # Random order

# Measure the execution time for worst case


start_time = time.time()
heapSort(worst_case_numbers)
worst_case_time = time.time() -
start_time

# Measure the execution time for best


case start_time = time.time()
heapSort(best_case_numbers)
best_case_time = time.time() -
start_time

# Measure the execution time for average


case start_time = time.time()
heapSort(average_case_numbers)
average_case_time = time.time() - start_time

print(f"Worst Case Time: {worst_case_time:.6f} seconds")


print(f"Best Case Time: {best_case_time:.6f} seconds")

6. Порівняльний аналіз теоретично та практично отриманих графіків


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

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

Теоретична складність алгоритму сортування купою підтверджується


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

7. Переваги та недоліки дослідженого Вами алгоритму сортування


та Ваші міркування

Переваги:
Не є рекурсивним та практично взагалі не вимагає додаткової пам’яті для
своєї роботи;
Алгоритм сортування за допомогою купи використовує структуру даних –
двійкову купу. Ця структура буває корисною і в інших ситуаціях,
наприклад, досить ефективно на її основі можна організовувати чергу з
пріоритетами;
Ефективність: Пірамідальне сортування має часову складність O(n * log n),
що робить його ефективним алгоритмом сортування для великої кількості
елементів. В порівнянні з іншими алгоритмами, такими як сортування
вставкою чи сортування бульбашкою, пірамідальне сортування зазвичай
працює швидше;
Алгоритм сортування за допомогою купи потребує T(n) = O(n·logn) часу
для сортування n об’єктів, та додаткову пам’ять розміром O(1), а не O(n),
як сортування злиттям;
Недоліки:

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

8. Розширені висновки з роботи


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

Основні кроки пірамідального сортування:

Побудова піраміди: Вихідний масив розглядається як повна бінарна купа.


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

Сортування: На цьому етапі найбільший (або найменший, залежно від


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

Часова складність: О(n log n) у всіх випадках, де n - розмір масиву.


Просторова складність: O(1), оскільки сортування відбувається без
створення додаткових масивів або структур даних.
Сортування здійснюється на місці, тобто не вимагає додаткової пам'яті
для зберігання сортованого масиву.

9. Порівняння алгоритмів сортування методом вставок,


бульбашковго сортування та сортування злиттям :
Алгоритм сортування методом вставок добре підходить для відносно
невеликих наборів даних, коли список вже відсортований або майже
відсортований.
Алгоритм бульбашкового сортування працює, порівнюючи пари сусідніх
елементів та обмінюючи їх, якщо вони не відсортовані. Цей алгоритм
працює добре для невеликих вхідних даних, але має часову складність
O(n^2), що робить його неефективним для великих наборів даних.
Сортування злиттям та пірамідальне сортування є більш ефективним
13
алгоритмом порівняно з методом вставок та бульбашковим сортуванням.
Сортування злиттям та пірамідальне сортування мають часову складність
O(n*log n), що робить їх ефективними для великих наборів даних. Вони
мають гарантовану часову складність навіть у найгіршому випадку.
Алгоритм сортування за допомогою купи потребує T(n) = O(n·logn) часу
для сортування n об’єктів, та додаткову пам’ять розміром O(1), а не O(n),
як сортування злиттям. Отже, цей алгоритм має переваги двох раніше
розглянутих алгоритмів – сортування злиттям і сортування вставками
Практично алгоритм сортування за допомогою купи не є найшвидшим.
Швидке сортування працює швидше.

14

You might also like