kzhereb / kpi-acts-ta2020

Materials for "Algorithm theory" course
MIT License
0 stars 0 forks source link

Q04.6. (*) Adaptability due to sub-algorithms. #59

Open Balukova opened 4 years ago

Balukova commented 4 years ago

*Q04.6. () Adaptability due to sub-algorithms.**

*Q04.6. () Адаптивність за рахунок під-алгоритмів**

PickDough commented 4 years ago

Для цього завдання я обрав QuickSort та MergeSort для перевірки їх роботи з адаптивними квадратичними алгоритмами та без. Щодо самих квадаратичних алгоритмів, то мною були обрані InsertionSort та SelectionSort. Тестування відбувалось на мові Python і я старавя виконати його з урахуванням ООП підходу, тому є 4 класи алгоритмів сортування з статичними методати та два класи тестування для парних та одинарних випадків. З усім кодом можна ознайомитись за цим посиланням

Тепер перейдемо до тестування :) Ось час, який був необхідний для алгоритмів, коли вони працювали самі по собі: image Я не запускав Selection та Insertion для розмірів більших за 10_000, оскільки очевидно, що це зайняло б багато часу.

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

image

Щодо пошуку найоптимальнішого числа для переходу, то було проведено перевірку для всіх пар з QuickSort та окремо всіх пар MergeSort і з них обрано середній для проміжку числе [6, 12]

image

image

Нарешті, я провів порівняльне тестування з та без використання адаптивного підходу:

image

image

Який з цього можна зрбити висновок? Якщо подивитись на останні скріншоти, то можна побачити, що виграш в часі адаптивного підходу присутній, порівняно з використанням лише nlogn алгоритмів, але на перший погляд важко сказати, що він надто суттєвий, проте можна також помітити, що зі збільшенням розміру масиву цей виграш у часі також збільшується. Ось приблизний виграш у часі у %, у MergeSort він доволі великий через виграш у невеликих масивах.

image

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

bastikk commented 4 years ago

Для реалізація цього завдання мною було вирішено використати сортування вставкою для невеликих масивів та швидке сортування для масивів значних розмірів. Для цього я реалізував дані алгоритми: `def insertionSort(arr): for i in range(1, len(arr)): key = arr[i] j = i - 1 while j >= 0 and key < arr[j]: arr[j + 1] = arr[j] j -= 1 arr[j + 1] = key return arr

def quickSort(massive): if len(massive) < 2: return massive middle = massive[0] less = [i for i in massive[1:] if i <= middle] more = [i for i in massive[1:] if i > middle] return quickSort(less) + [middle] + quickSort(more)`

Далі переді мною постало завдання визначити розмір масиву до якого слід застосовувати сортування вставкою, а після якого слід використовувати швидке сортування. Визначення цього числа проводилося експериментальним шляхом. Для точності створювалося 10 масивів рандомно заповнені n елементами. Далі ви можете побачити заміри часу. Було вирішено зупинитися на числі 650, адже саме при такому розмірі массива, вид сортування майже не грає роль. 10:

image

100:

image

1000:

image

650:

image

Після визначення даного числа ми створили функцію адаптивного алгоритма. def adaptiveSort(): start_time = datetime.now() for arr in arrays: if len(arr)>650: quickSort(arr) else: insertionSort(arr) print("AdaptiveSort used:", datetime.now() - start_time)

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

image

Нижче ви можете ознайомитися з повним кодом програми. `import random from datetime import datetime import sys

sys.setrecursionlimit(50000)

def insertionSort(arr): for i in range(1, len(arr)): key = arr[i] j = i - 1 while j >= 0 and key < arr[j]: arr[j + 1] = arr[j] j -= 1 arr[j + 1] = key return arr

def quickSort(massive): if len(massive) < 2: return massive middle = massive[0] less = [i for i in massive[1:] if i <= middle] more = [i for i in massive[1:] if i > middle] return quickSort(less) + [middle] + quickSort(more)

arrays = [] for i in range(10): arr = [] for j in range(random.randint(0, 1000)): arr.append(random.randint(0, 100000000000)) arrays.append(arr)

start_time = datetime.now() for arr in arrays: insertionSort(arr) print("InsertionSort used:", datetime.now() - start_time)

start_time = datetime.now() for arr in arrays: quickSort(arr) print("QuickSort used:", datetime.now() - start_time)

def adaptiveSort(): start_time = datetime.now() for arr in arrays: if len(arr)>650: quickSort(arr) else: insertionSort(arr) print("AdaptiveSort used:", datetime.now() - start_time)

adaptiveSort() `