diff --git a/README.md b/README.md index 74f925a..bea946e 100644 --- a/README.md +++ b/README.md @@ -57,8 +57,12 @@ Mesurez combien de temps prend python à générer un tableau composés de : Sur un tableur, générez un tableau permettant de visualiser le temps d'éxécution en fonction de la taille de l'entrée. + + Comment vous semble évoluer la courbe ? Observez bien les différentes courbes du graphique ci-dessous. Quelle est la plus ressemblante à notre situation ? *Écrivez votre réponse ici* +### La courbe semble être une droite. Cela signifie que le temps d'éxécution est proportionnel à la taille de l'entrée. La complexité semble linéaire et ressemble à la courbe de complexité O(N). + #### Quelques exemples de complexités courante : @@ -77,6 +81,14 @@ Observez attentivement l'animation de tri par sélection ci-dessous pour en comp Écrivez en français classique ce que vous voyez. Quel est le fonctionnement ? Comment l'expliqueriez-vous à quelqu'un ? *Écrivez votre réponse ici* +Le fonctionnement de ce tri est le suivant : + +* Vous commencez par regarder toutes les valeurs et vous trouvez la plus petite. +* Une fois que vous avez identifié la plus petite valeur vous la mettez au tout début de la liste ( dans le cas où on trie dans l'ordre croissant comme ici par exemple ). +* Ensuite, vous regardez les valeurs restantes et vous trouvez la nouvelle valeur la plus petite. +* Vous la mettez au début de la liste juste après la précédente valeur triée. +* Vous répétez ce processus jusqu'à ce que toutes les valeurs soient triées. + Puis implémentez l'algorithme en python dans la fonction `sort` du fichier `sort/selection.py`. Vérifiez son bon fonctionnement en éxécutant le fichier `python3 -m unittest`. Le test correspondant au tri par sélection doit passer. Mesurez le temps d'éxécution pour un tableau de : @@ -88,8 +100,13 @@ Mesurez le temps d'éxécution pour un tableau de : Tracez le graphique correspondant. + + Quelle semble être la complexité de notre fonction de tri ? Cela est-il logique par rapport au code que vous avez implémenté ? *Écrivez votre réponse ici* +### La complexité semble être quadratique ( O(N²) ). Cela est logique par rapport au code que nous avons implémenté car nous avons deux boucles imbriquées. + + ### 2. Tri par insertion Observez attentivement l'animation de tri par insertion ci-dessous pour en comprendre le fonctionnement. @@ -98,6 +115,14 @@ Observez attentivement l'animation de tri par insertion ci-dessous pour en compr Écrivez en français classique ce que vous voyez. Quel est le fonctionnement ? Comment l'expliqueriez-vous à quelqu'un ? *Écrivez votre réponse ici* +Le fonctionnement de ce tri est le suivant : + +* Vous prenez le premier élément de la liste et vous le considérez comme une liste triée de un élémént. +* Ensuite vous parcourez le reste de la liste et vous insérez chaque élément dans la liste triée de manière à ce que la liste reste triée. +* Pour insérer un élément dans la liste triée, vous parcourez la liste triée de la fin vers le début et vous décalez les éléments jusqu'à ce que vous trouviez la bonne place pour insérer l'élément. +* Vous répétez ce processus jusqu'à ce que tous les éléments soient triés. + + Puis implémentez l'algorithme en python dans la fonction `sort` du fichier `sort/insertion.py`. Utilisez les tests automatiques pour vérifier votre implémentation. Mesurez le temps d'éxécution pour un tableau de : @@ -109,8 +134,12 @@ Mesurez le temps d'éxécution pour un tableau de : Tracez le graphique correspondant. + + Quelle semble être la complexité de notre fonction de tri ? Cela est-il logique par rapport au code que vous avez implémenté ? *Écrivez votre réponse ici* +### La complexité semble aussi être quadratique ( O(N²) ). Cela est logique par rapport au code que nous avons implémenté car nous avons également deux boucles imbriquées. + ### 3. Tri par fusion Le tri par fusion est plus complexe : il utilise en effet la récursion, c'est à dire une fonction qui s'appelle elle-même. @@ -156,6 +185,13 @@ Cet algorithme est de type "diviser pour régner". Écrivez en français classique ce que vous voyez. Quel est le fonctionnement ? Comment l'expliqueriez-vous à quelqu'un ? *Écrivez votre réponse ici* +Le fonctionnement du tri par fusion est le suivant : + +* Vous prenez la liste à trier et vous la divisez en deux listes. +* Vous répétez ce processus jusqu'à ce que vous ayez des listes de taille 1. +* Ensuite vous fusionnez les listes deux par deux en les triant. +* Vous répétez ce processus jusqu'à ce que vous ayez une seule liste triée. + Complétez la fonction `sort` du fichier `sort/fusion.py` en suivant les instructions suivantes. Il vous faudra deux fonctions : @@ -174,16 +210,26 @@ Mesurez le temps d'éxécution pour un tableau de : Tracez le graphique correspondant. + + Quelle semble être la complexité de notre fonction de tri ? Cela est-il logique par rapport au code que vous avez implémenté ? *Écrivez votre réponse ici* +### La complexité semble être linéarithmique ( O(N log(N)) ). Cela est logique par rapport au code que nous avons implémenté car nous avons une boucle qui divise le tableau en deux et une autre boucle qui fusionne les tableaux. + Question bonus : Y a-t-il des tailles de tableaux pour lesquelles le tri par fusion n'est pas aussi rapide que les précédents tris abordés ? *Écrivez votre réponse ici* +### Le tri par fusion est plus rapide que les autres tris abordés pour toutes les tailles de tableaux. + ### 4. sort() Bien que tout cela soit fascinant, Python possède sa propre méthode de tri : `sort()`. Une dernière fois, analysez le temps d'exécution et découvrez si python fait mieux que nos implémentations rudimentaires ;) +Graphique pour la fonction `sort()` de python : + + + ## Pour rendre ce TP Merci de faire une Pull Request vers ce repository. diff --git a/__main__.py b/__main__.py index 98a585e..a02c75a 100644 --- a/__main__.py +++ b/__main__.py @@ -1,5 +1,65 @@ +import time +from typing import Callable +from typing import TypedDict + +from sort.fusion import sort as fusion_sort +from sort.insertion import sort as insertion_sort +from sort.range import generate_array_of_number +from sort.selection import sort as selection_sort + +algorithms = ( + insertion_sort, + selection_sort, + fusion_sort, +) + +array_sizes = (1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000) +array_sizes_generate_array = (1_000_000, 2_000_000, 3_000_000, 4_000_000, + 5_000_000, 6_000_000, 7_000_000, 8_000_000, + 9_000_000, 10_000_000) + + +class FunctionMetrics(TypedDict): + """Function metrics""" + name: str + sort_metrics: dict[int, float] + + +def get_metrics(sort_function: Callable) -> dict[int, float]: + """Get metrics for sort functions""" + time_metric: dict[int, float] = {} + for array_size in array_sizes: + start_time = time.time() + print(f"Testing for array size: {array_size}") + sort_function(generate_array_of_number(array_size)) + end_time = time.time() + time_metric[array_size] = end_time - start_time + return time_metric + + def main(): - print("Hello world") + + # Used to test the time it takes to sort lists using python sort function + for array_size in array_sizes: + start_time = time.time() + generate_array_of_number(array_size).sort() + end_time = time.time() + print(end_time - start_time) + + # Used to test the time it takes to sort lists using the fusion sort, + # selection sort and insertion sort algorithms + metrics: list[FunctionMetrics] = [] + for algorithm in algorithms: + metrics.append({ + "name": algorithm.__name__, + "sort_metrics": get_metrics(algorithm) + }) + + # Print metrics + for metric in metrics: + print(f"Metrics for {metric['name']}") + for array_size, time_metric in metric["sort_metrics"].items(): + print(f"Array size: {array_size} - Time: {time_metric}") main() diff --git a/img/Courbe_Fusion_sort.png b/img/Courbe_Fusion_sort.png new file mode 100644 index 0000000..2e81b19 Binary files /dev/null and b/img/Courbe_Fusion_sort.png differ diff --git a/img/Courbe_Insertion_sort.png b/img/Courbe_Insertion_sort.png new file mode 100644 index 0000000..aef4fa2 Binary files /dev/null and b/img/Courbe_Insertion_sort.png differ diff --git a/img/Courbe_Python_sort.png b/img/Courbe_Python_sort.png new file mode 100644 index 0000000..08692df Binary files /dev/null and b/img/Courbe_Python_sort.png differ diff --git a/img/Courbe_Selection_sort.png b/img/Courbe_Selection_sort.png new file mode 100644 index 0000000..78a8c7e Binary files /dev/null and b/img/Courbe_Selection_sort.png differ diff --git a/img/Tableau_Range.png b/img/Tableau_Range.png new file mode 100644 index 0000000..69bb40d Binary files /dev/null and b/img/Tableau_Range.png differ diff --git a/sort/fusion.py b/sort/fusion.py index 73a21d3..5d3d7ae 100644 --- a/sort/fusion.py +++ b/sort/fusion.py @@ -1,2 +1,38 @@ -def sort(array: list[int]) -> list[int]: - return array +def sort(input_list: list[int]) -> list[int]: + """ + Merge sort algorithm + + :param input_list: The list to be sorted. + :return: The sorted list. + """ + if len(input_list) <= 1: + return input_list + + # Split input_list in half + middle = len(input_list) // 2 + left = sort(input_list[:middle]) + right = sort(input_list[middle:]) + + return merge(left, right) + + +def merge(left_list: list[int], right_list: list[int]) -> list[int]: + """Merge two sorted lists into one sorted list""" + merged_result = [] + left_index = right_index = 0 + + # Compare elements from left and right lists and add the smallest + # one to the result + while left_index < len(left_list) and right_index < len(right_list): + if left_list[left_index] < right_list[right_index]: + merged_result.append(left_list[left_index]) + left_index += 1 + else: + merged_result.append(right_list[right_index]) + right_index += 1 + + # Append the remaining elements from left and right lists + merged_result.extend(left_list[left_index:]) + merged_result.extend(right_list[right_index:]) + + return merged_result diff --git a/sort/insertion.py b/sort/insertion.py index 73a21d3..bb3f149 100644 --- a/sort/insertion.py +++ b/sort/insertion.py @@ -1,2 +1,25 @@ -def sort(array: list[int]) -> list[int]: - return array +def sort(input_list: list[int]) -> list[int]: + """ + Insertion sort algorithm + + :param input_list: The list to be sorted. + :return: The sorted list. + """ + for current_index in range(1, len(input_list)): + current_value = input_list[current_index] + position = current_index + + # Find the right position for current_value + while position > 0 and current_value < input_list[position - 1]: + position -= 1 + + # Once the right position is found, shift all elements between + # position and current_index to the right by one. + if position != current_index: + for i in range(current_index, position, -1): + input_list[i] = input_list[i - 1] + + # Place the current_value at its correct position + input_list[position] = current_value + + return input_list diff --git a/sort/range.py b/sort/range.py index fc252ab..033c19c 100644 --- a/sort/range.py +++ b/sort/range.py @@ -1,2 +1,11 @@ +import random + + def generate_array_of_number(array_size: int) -> list[int]: - return [] + """ + Generates an array of random numbers + + :param array_size: + :return: array of random numbers + """ + return [random.randint(0, 100) for i in range(array_size)] diff --git a/sort/recursion.py b/sort/recursion.py index e7f4320..b15df85 100644 --- a/sort/recursion.py +++ b/sort/recursion.py @@ -1,2 +1,8 @@ def get_factorial(number: int) -> int: - return number + """ + Gets the factorial of a number. + + :param number: + :return: factorial of a number + """ + return 1 if number == 0 else number * get_factorial(number - 1) diff --git a/sort/selection.py b/sort/selection.py index 73a21d3..f950c37 100644 --- a/sort/selection.py +++ b/sort/selection.py @@ -1,2 +1,25 @@ -def sort(array: list[int]) -> list[int]: - return array +def sort(input_list: list[int]) -> list[int]: + """ + Selection sort algorithm + + :param input_list: The list to be sorted. + :return: The sorted list. + """ + # Iterate over the entire list + for current_index in range(len(input_list)): + # Assume the current index is the index of the smallest element + min_index = current_index + + # Iterate over the rest of the list + for comparison_index in range(current_index + 1, len(input_list)): + # If a smaller element is found, update min_index + if input_list[min_index] > input_list[comparison_index]: + min_index = comparison_index + + # Swap the current element with the smallest element found in + # the unsorted part of the list + input_list[current_index], input_list[min_index] = input_list[ + min_index], input_list[current_index] + + # Return the sorted list + return input_list