From 93311c4ad5602b2ec17e8b8f9a3f8b0c490c03c8 Mon Sep 17 00:00:00 2001 From: Piotr Kozak Date: Tue, 6 May 2025 19:02:00 +0200 Subject: [PATCH] Added avl height benchmark --- Lab1/benchmark.py | 3 +- Lab1/test_data.txt | 6 -- Lab2/.gitignore | 2 +- Lab2/avl.csv | 26 ++++++ Lab2/avl/avl.cpp | 86 +++++++++++++++++++ Lab2/avl/avl.h | 13 +++ Lab2/benchmark.cpp | 208 +++++++++++++++++++++++++++------------------ Lab2/bst/bst.cpp | 8 ++ Lab2/bst/bst.h | 1 + Lab2/plot.py | 17 +++- Lab2/results.csv | 51 +++++++++++ flake.nix | 1 + 12 files changed, 328 insertions(+), 94 deletions(-) delete mode 100644 Lab1/test_data.txt create mode 100644 Lab2/avl.csv create mode 100644 Lab2/avl/avl.cpp create mode 100644 Lab2/avl/avl.h create mode 100644 Lab2/results.csv diff --git a/Lab1/benchmark.py b/Lab1/benchmark.py index 7a5c8bb..d3f9f0e 100644 --- a/Lab1/benchmark.py +++ b/Lab1/benchmark.py @@ -44,8 +44,7 @@ def plot(program_name, sequences, array_size): plt.savefig(f"./benchmarks/{program_name}.png") if __name__ == "__main__": - # program_names = ["insertion", "selection", "heapsort", "mergesort"] - program_names = ["heapsort"] + program_names = ["insertion", "selection", "heapsort", "mergesort"] sequence_types = ["random", "sorted", "reversed", "constant", "v_shaped"] array_sizes = [i for i in range(1000, 15001, 1000)] tests = 10 diff --git a/Lab1/test_data.txt b/Lab1/test_data.txt deleted file mode 100644 index 37bb752..0000000 --- a/Lab1/test_data.txt +++ /dev/null @@ -1,6 +0,0 @@ -5 -12 -65 -233 -87 -23 diff --git a/Lab2/.gitignore b/Lab2/.gitignore index aca22fe..c3226e8 100644 --- a/Lab2/.gitignore +++ b/Lab2/.gitignore @@ -1,3 +1,3 @@ benchmark -results.csv +*.csv charts/ diff --git a/Lab2/avl.csv b/Lab2/avl.csv new file mode 100644 index 0000000..a410d07 --- /dev/null +++ b/Lab2/avl.csv @@ -0,0 +1,26 @@ +Structure,Height +2000,10 +4000,11 +6000,12 +8000,12 +10000,13 +12000,13 +14000,13 +16000,13 +18000,14 +20000,14 +22000,14 +24000,14 +26000,14 +28000,14 +30000,14 +32000,14 +34000,15 +36000,15 +38000,15 +40000,15 +42000,15 +44000,15 +46000,15 +48000,15 +50000,15 diff --git a/Lab2/avl/avl.cpp b/Lab2/avl/avl.cpp new file mode 100644 index 0000000..6db08ea --- /dev/null +++ b/Lab2/avl/avl.cpp @@ -0,0 +1,86 @@ +#include "avl.h" +#include "algorithm" + +int getHeight(AVL_tree *root) { + if (!root) + return 0; + return root->height; +} + +AVL_tree *rightRotate(AVL_tree *y) { + AVL_tree *x = y->left; + AVL_tree *T2 = x->right; + + x->right = y; + y->left = T2; + + y->height = 1 + std::max(getHeight(y->left), getHeight(y->right)); + x->height = 1 + std::max(getHeight(x->left), getHeight(x->right)); + + return x; +} + +AVL_tree *leftRotate(AVL_tree *x) { + AVL_tree *y = x->right; + AVL_tree *T2 = y->left; + + y->left = x; + x->right = T2; + + x->height = 1 + std::max(getHeight(x->left), getHeight(x->right)); + y->height = 1 + std::max(getHeight(y->left), getHeight(y->right)); + + return y; +} + +int getBalance(AVL_tree *root) { + if (!root) + return 0; + return getHeight(root->left) - getHeight(root->right); +} + +AVL_tree *insertAVL(AVL_tree *root, int value) { + if (root == nullptr) { + return new AVL_tree{value, nullptr, nullptr, 1}; + } + + if (value < root->info) { + root->left = insertAVL(root->left, value); + } else if (value > root->info) { + root->right = insertAVL(root->right, value); + } else { + return root; + } + + root->height = 1 + std::max(getHeight(root->left), getHeight(root->right)); + + int balance = getBalance(root); + + if (balance > 1 && value < root->left->info) { + return rightRotate(root); + } + + if (balance < -1 && value > root->right->info) { + return leftRotate(root); + } + + if (balance > 1 && value > root->left->info) { + root->left = leftRotate(root->left); + return rightRotate(root); + } + + if (balance < -1 && value < root->right->info) { + root->right = rightRotate(root->right); + return leftRotate(root); + } + + return root; +} + +void deleteAVL(AVL_tree *root){ + if (root != nullptr) { + deleteAVL(root->left); + deleteAVL(root->right); + delete root; + } +} diff --git a/Lab2/avl/avl.h b/Lab2/avl/avl.h new file mode 100644 index 0000000..86899a5 --- /dev/null +++ b/Lab2/avl/avl.h @@ -0,0 +1,13 @@ +struct AVL_tree { + int info; + AVL_tree *left; + AVL_tree *right; + int height; +}; + +int getHeight(AVL_tree *root); +AVL_tree *insertAVL(AVL_tree *root, int value); +AVL_tree *rotateLeft(AVL_tree *root); +AVL_tree *rotateRight(AVL_tree *root); +AVL_tree *searchAVL(AVL_tree *root, int value); +void deleteAVL(AVL_tree *root); diff --git a/Lab2/benchmark.cpp b/Lab2/benchmark.cpp index c25bccd..322a131 100644 --- a/Lab2/benchmark.cpp +++ b/Lab2/benchmark.cpp @@ -1,97 +1,123 @@ +#include "avl/avl.h" #include "bst/bst.h" #include "list/list.h" +#include #include #include -#include +#include #include -#include +#include -void measureList(std::set *sequence, FILE *file){ - float buildTime = 0, searchTime = 0, deleteTime = 0; +void measureList(std::set *sequence, FILE *file) { + float buildTime = 0, searchTime = 0, deleteTime = 0; - for (int i = 0; i < 10; i++) { - std::cout << "- Running list " << i + 1 << "/10\r"; - fflush(stdout); + for (int i = 0; i < 10; i++) { + std::cout << "- Running list " << i + 1 << "/10\r"; + fflush(stdout); - // Measure build time - auto start = std::chrono::high_resolution_clock::now(); - List *head = nullptr; - for (int value : *sequence) { - head = insert(head, value); - } - auto end = std::chrono::high_resolution_clock::now(); - std::chrono::duration elapsed_seconds = end - start; - buildTime += elapsed_seconds.count() * 1000; // Convert to milliseconds - - // Now we run the search - start = std::chrono::high_resolution_clock::now(); - for (int value : *sequence) { - search(head, value); - } - end = std::chrono::high_resolution_clock::now(); - elapsed_seconds = end - start; - searchTime += elapsed_seconds.count() * 1000; // Convert to milliseconds - - // Measure deletion time for list - start = std::chrono::high_resolution_clock::now(); - while(head != nullptr){ - head = remove(head); - } - end = std::chrono::high_resolution_clock::now(); - elapsed_seconds = end - start; - deleteTime += elapsed_seconds.count() * 1000; // Convert to milliseconds + // Measure build time + auto start = std::chrono::high_resolution_clock::now(); + List *head = nullptr; + for (int value : *sequence) { + head = insert(head, value); } + auto end = std::chrono::high_resolution_clock::now(); + std::chrono::duration elapsed_seconds = end - start; + buildTime += elapsed_seconds.count() * 1000; // Convert to milliseconds - buildTime /= 10; - searchTime /= 10; - deleteTime /= 10; + // Now we run the search + start = std::chrono::high_resolution_clock::now(); + for (int value : *sequence) { + search(head, value); + } + end = std::chrono::high_resolution_clock::now(); + elapsed_seconds = end - start; + searchTime += elapsed_seconds.count() * 1000; // Convert to milliseconds - std::cout << "- List built in " << buildTime << "ms | searched in " << searchTime << "ms | deleted in " << deleteTime << "ms" << std::endl; - fprintf(file, "List,%f,%f,%f\n", buildTime, searchTime, deleteTime); + // Measure deletion time for list + start = std::chrono::high_resolution_clock::now(); + while (head != nullptr) { + head = remove(head); + } + end = std::chrono::high_resolution_clock::now(); + elapsed_seconds = end - start; + deleteTime += elapsed_seconds.count() * 1000; // Convert to milliseconds + } + + buildTime /= 10; + searchTime /= 10; + deleteTime /= 10; + + std::cout << "- List built in " << buildTime << "ms | searched in " + << searchTime << "ms | deleted in " << deleteTime << "ms" + << std::endl; + fprintf(file, "List,%f,%f,%f\n", buildTime, searchTime, deleteTime); } -void measureBST(std::set *sequence, FILE *file){ - float buildTime = 0, searchTime = 0, deleteTime = 0; +void measureBST(std::set *sequence, FILE *file) { + float buildTime = 0, searchTime = 0, deleteTime = 0; - for (int i = 0; i < 10; i++) { - std::cout << "- Running bst " << i + 1 << "/10\r"; - fflush(stdout); + for (int i = 0; i < 10; i++) { + std::cout << "- Running bst " << i + 1 << "/10\r"; + fflush(stdout); - // Measure build time - auto start = std::chrono::high_resolution_clock::now(); - Tree *root = nullptr; - for (int value : *sequence) { - root = insert(root, value); - } - root = balance(root); - auto end = std::chrono::high_resolution_clock::now(); - std::chrono::duration elapsed_seconds = end - start; - buildTime += elapsed_seconds.count() * 1000; // Convert to milliseconds - - // Now we run the search - start = std::chrono::high_resolution_clock::now(); - for (int value : *sequence) { - search(root, value); - } - end = std::chrono::high_resolution_clock::now(); - elapsed_seconds = end - start; - searchTime += elapsed_seconds.count() * 1000; // Convert to milliseconds - - // Measure deletion time for tree - start = std::chrono::high_resolution_clock::now(); - deleteTree(root); - end = std::chrono::high_resolution_clock::now(); - elapsed_seconds = end - start; - deleteTime += elapsed_seconds.count() * 1000; // Convert to milliseconds + // Measure build time + auto start = std::chrono::high_resolution_clock::now(); + Tree *root = nullptr; + for (int value : *sequence) { + root = insert(root, value); } + root = balance(root); + auto end = std::chrono::high_resolution_clock::now(); + std::chrono::duration elapsed_seconds = end - start; + buildTime += elapsed_seconds.count() * 1000; // Convert to milliseconds - buildTime /= 10; - searchTime /= 10; - deleteTime /= 10; + // Now we run the search + start = std::chrono::high_resolution_clock::now(); + for (int value : *sequence) { + search(root, value); + } + end = std::chrono::high_resolution_clock::now(); + elapsed_seconds = end - start; + searchTime += elapsed_seconds.count() * 1000; // Convert to milliseconds - std::cout << "- Tree built in " << buildTime << "ms | searched in " << searchTime << "ms | deleted in " << deleteTime << "ms" << std::endl; - fprintf(file, "BST,%f,%f,%f\n", buildTime, searchTime, deleteTime); + // Measure deletion time for tree + start = std::chrono::high_resolution_clock::now(); + deleteTree(root); + end = std::chrono::high_resolution_clock::now(); + elapsed_seconds = end - start; + deleteTime += elapsed_seconds.count() * 1000; // Convert to milliseconds + } + + buildTime /= 10; + searchTime /= 10; + deleteTime /= 10; + + std::cout << "- Tree built in " << buildTime << "ms | searched in " + << searchTime << "ms | deleted in " << deleteTime << "ms" + << std::endl; + fprintf(file, "BST,%f,%f,%f\n", buildTime, searchTime, deleteTime); +} + +void benchmarkAVL(std::set *sequence, FILE *file) { + Tree *bst = nullptr; + for (int value : *sequence) { + bst = insert(bst, value); + } + + int heightBST = getHeight(bst, 0); + deleteTree(bst); + + AVL_tree *avl = nullptr; + for (int value : *sequence) { + avl = insertAVL(avl, value); + } + + int heightAVL = getHeight(avl); + deleteAVL(avl); + + fprintf(file, "%d,%d\n", heightBST, heightAVL); } int main() { @@ -100,9 +126,25 @@ int main() { std::mt19937 gen(rd()); std::uniform_int_distribution<> dis(1, 1000000); - // Open a file for writing results - FILE *file = fopen("results.csv", "w"); - fprintf(file, "Elements,Structure,BuildTime,SearchTime,DeleteTime\n"); + int mode = 1; + std::cout << "Select benchmark to run:\n 1 - Benchmark List vs BST\n 2 - " + "Benchmark BST vs AVL" + << std::endl; + std::cin >> mode; + + if (mode < 1 && mode > 2) { + mode = 1; + } + + FILE *file; + if (mode == 1) { + // Open a file for writing results + file = fopen("results.csv", "w"); + fprintf(file, "Structure,BuildTime,SearchTime,DeleteTime\n"); + } else { + file = fopen("avl.csv", "w"); + std::fprintf(file, "Structure,Height\n"); + } for (int n = 1; n < 26; n++) { std::set sequence; @@ -113,12 +155,14 @@ int main() { // Display this like a cascade std::cout << "Running tests for " << n * 1000 << " elements..." << std::endl; - - measureList(&sequence, file); - measureBST(&sequence, file); + if (mode == 1) { + measureList(&sequence, file); + measureBST(&sequence, file); + } else { + benchmarkAVL(&sequence, file); + } } fclose(file); - return 0; } diff --git a/Lab2/bst/bst.cpp b/Lab2/bst/bst.cpp index 4431b44..aeb114e 100644 --- a/Lab2/bst/bst.cpp +++ b/Lab2/bst/bst.cpp @@ -58,3 +58,11 @@ void deleteTree(Tree *root) { delete root; } } + +int getHeight(Tree *root, int height) { + if (root == nullptr) return height; + height += 1; + int leftHeight = getHeight(root->left, height); + int rightHeight = getHeight(root->right, height); + return std::max(leftHeight, rightHeight) + 1; +} diff --git a/Lab2/bst/bst.h b/Lab2/bst/bst.h index 4c5e56a..c5ddf7f 100644 --- a/Lab2/bst/bst.h +++ b/Lab2/bst/bst.h @@ -12,3 +12,4 @@ void traverseInOrder(Tree *root,std::vector &vec); Tree *rebuild(std::vector *vec, int start, int end); Tree *balance(Tree *root); void deleteTree(Tree *root); +int getHeight(Tree *root, int height); diff --git a/Lab2/plot.py b/Lab2/plot.py index 493df3c..c7f6e34 100644 --- a/Lab2/plot.py +++ b/Lab2/plot.py @@ -1,9 +1,9 @@ import matplotlib.pyplot as plt -def plot(header:str, first:list[int], second:list[int], log=False): +def plot(header:str, first:list[int], second:list[int], log=False, labels=("Lista", "Drzewo BST")): plt.figure(figsize=(10, 6)) - plt.plot(range(1000,25001,1000), first, label="Lista") - plt.plot(range(1000,25001,1000), second, label="Drzewo BST") + plt.plot(range(1000,25001,1000), first, label=labels[0]) + plt.plot(range(1000,25001,1000), second, label=labels[1]) plt.xlabel("Rozmiar tablicy") plt.ylabel("Czas (ms)") if log: @@ -45,3 +45,14 @@ if __name__ == "__main__": plot("Tworzenie", listTimes['build'], bstTimes['build']) plot("Wyszukiwanie", listTimes['search'], bstTimes['search'], log=True) plot("Usuwanie", listTimes['delete'], bstTimes['delete']) + + with open("avl.csv", "r") as file: + data = file.read() + lines = data.split('\n') + headers = lines[0].split(',') + values = [line.split(',') for line in lines[1:-1]] + + bstHeights = [int(x[0]) for x in values] + avlHeights = [int(x[1]) for x in values] + + plot("AVl", bstHeights, avlHeights, log=True, labels=("Drzewo BST", "Drzewo AVL")) diff --git a/Lab2/results.csv b/Lab2/results.csv new file mode 100644 index 0000000..ff07b06 --- /dev/null +++ b/Lab2/results.csv @@ -0,0 +1,51 @@ +Elements,Structure,BuildTime,SearchTime,DeleteTime +List,0.543320,0.520767,0.010243 +BST,1.544223,0.025997,0.008951 +List,3.479965,3.461854,0.022565 +BST,9.890946,0.142389,0.026754 +List,7.862325,7.733483,0.037077 +BST,20.342850,0.257188,0.048077 +List,14.756193,14.451564,0.044844 +BST,37.088211,0.312305,0.054895 +List,22.063259,21.787954,0.056064 +BST,51.029690,0.396568,0.065577 +List,26.197474,26.072092,0.062295 +BST,73.239220,0.458283,0.074890 +List,39.307865,38.355278,0.069399 +BST,107.733963,0.671136,0.093605 +List,59.704353,59.331749,0.093153 +BST,164.595337,1.058345,0.120158 +List,75.755898,74.383286,0.101044 +BST,209.177338,1.228530,0.138253 +List,94.265694,93.193260,0.122636 +BST,270.258728,1.591327,0.164845 +List,104.814880,101.211784,0.116452 +BST,246.079788,0.931309,0.133289 +List,112.470276,110.182449,0.117044 +BST,276.451080,0.975446,0.137597 +List,126.718018,124.866295,0.121890 +BST,315.444519,1.054176,0.142032 +List,145.154541,141.603271,0.127330 +BST,362.195190,1.103273,0.145592 +List,168.838623,166.042252,0.132073 +BST,402.726440,1.198986,0.153952 +List,193.290436,189.238907,0.140827 +BST,487.910156,1.276635,0.158837 +List,220.266205,214.658295,0.146799 +BST,504.256195,1.505627,0.168319 +List,241.377975,235.950562,0.152117 +BST,580.977173,1.557522,0.173307 +List,266.187561,264.576538,0.177301 +BST,724.846436,2.546352,0.223694 +List,306.509338,306.340973,0.191684 +BST,785.615173,2.440228,0.220973 +List,326.952972,319.279846,0.188148 +BST,793.774536,1.972353,0.209726 +List,347.416840,340.725769,0.190998 +BST,952.154907,2.114705,0.209531 +List,374.497253,370.216095,0.194421 +BST,887.039246,2.150400,0.221431 +List,405.752655,403.273956,0.201055 +BST,1040.535645,2.334295,0.223531 +List,449.462952,439.051361,0.209022 +BST,1124.903564,2.481752,0.235188 diff --git a/flake.nix b/flake.nix index 2a1088b..a3a536f 100644 --- a/flake.nix +++ b/flake.nix @@ -19,6 +19,7 @@ [ clang-tools cmake + libgcc codespell conan cppcheck