diff --git a/README.md b/README.md index 74f925a..c326b78 100644 --- a/README.md +++ b/README.md @@ -75,7 +75,10 @@ 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* +Écrivez en français classique ce que vous voyez. Quel est le fonctionnement ? Comment l'expliqueriez-vous à quelqu'un ? +Selon moi le tri par selection va regarder toute la liste, +il va selectionner l'element le plus petit et le placer au début de la liste. +Ensuite, il recommence avec le reste de la liste tant qu'elle n'est pas vide. 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. @@ -88,7 +91,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* +Quelle semble être la complexité de notre fonction de tri ? Cela est-il logique par rapport au code que vous avez implémenté ? +Le tri par selection est de complexité O(N**2) +Car entre la première et la dernière valeur les entrées sont multiplé par 10 +alors que les temps sont multipliés par 100. +De plus on a directement une double boucle dans le programme +qui regarde tout les éléments, ce qui fait N*N donc N**2 ### 2. Tri par insertion @@ -96,7 +104,10 @@ 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* +Écrivez en français classique ce que vous voyez. Quel est le fonctionnement ? Comment l'expliqueriez-vous à quelqu'un ? +Selon moi, Le tri par insertion va regarder le deuxieme element de la liste +et le placer avant ou après le premier si il est plus petit ou non. Puis faire +pareil avec les autres éléments en les comparant avec tous ceux avant 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. @@ -109,7 +120,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* +Quelle semble être la complexité de notre fonction de tri ? Cela est-il logique par rapport au code que vous avez implémenté ? +Le tri par insertion est de complexité O(N**2) +Car entre la première et la dernière valeur les entrées sont multiplé par 10 +alors que les temps sont multipliés par 100. +De plus on a directement une double boucle dans le programme +qui regarde tout les éléments, ce qui fait N*N donc N**2 ### 3. Tri par fusion @@ -154,7 +170,14 @@ Observez bien le schéma suivant : il représente le concept du tri par fusion. 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* +Écrivez en français classique ce que vous voyez. Quel est le fonctionnement ? Comment l'expliqueriez-vous à quelqu'un ? +Le tri fusion prend un tableau. +Si le tableau n'est pas vide ou n'est pas composé d'un seul élément, +Python vas le divisé en deux en rappelant la meme fonction puis le merger. +A chaque fois avant de merger les différents tableaux vont se réduire, +jusqu'à ce que chaque tableau n'ait qu'un élément. +Ensuite les tableaux vont se merger petit à petit en restant croissant et +en formant au final un seul tableau Complétez la fonction `sort` du fichier `sort/fusion.py` en suivant les instructions suivantes. @@ -174,9 +197,20 @@ 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* - -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* +Quelle semble être la complexité de notre fonction de tri ? Cela est-il logique par rapport au code que vous avez implémenté ? +Le tri fusion a l'air d'etre de complexité N*log(N) +car on subdivise le tri en plusieurs tri de 1 élément. +Puis on les recombine ensemble en les lisant. +Le nombre d'étape ou on lit chaque valeur est +a chaque fois que l'on merge des élements (2**(k))>=n>(2**(k-1))+1. +Il a donc k étapes ou on lit tout n. k est plus petit que N +car on prend le k supérieur ou égal à N + +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 ? +Si on considère que le tri par fusion a une complexité inférieur aux autres tri +alors plus le nombre de valeur va augmenté +plus l'écart entre les différentes complexités va augmenté également. +Le tri sera toujours plus rapide qu'un tri avec une complexité supérieur ### 4. sort() @@ -184,6 +218,9 @@ Bien que tout cela soit fascinant, Python possède sa propre méthode de tri : ` Une dernière fois, analysez le temps d'exécution et découvrez si python fait mieux que nos implémentations rudimentaires ;) +Le tri sort de python est environ 100 fois plus rapide que notre tri fusion. +Sa complexité semble etre N + ## Pour rendre ce TP Merci de faire une Pull Request vers ce repository. diff --git a/__main__.py b/__main__.py index 98a585e..a626ee7 100644 --- a/__main__.py +++ b/__main__.py @@ -1,5 +1,135 @@ -def main(): - print("Hello world") +import time +from enum import Enum, unique, auto +from sort.range import generate_array_of_number +from sort.selection import sort as selection_sort +from sort.insertion import sort as insertion_sort +from sort.recursion import get_factorial +from sort.fusion import sort as fusion_sort -main() +@unique +class Function(Enum): + GENERATE_ARRAY = auto() + SELECTION_SORT = auto() + INSERTION_SORT = auto() + FACTORIAL = auto() + FUSION_SORT = auto() + PYTHON_SORT = auto() + + +# We create a loop where there are all the element we need for test our sort +LOOP_REQUIRED = list[int](((element % 10)+1) * 10 if element < 10 + else + ((element % 10)+1) * 1_000 if 10 <= element < 20 + else + ((element % 10)+1) * 10_000 if 20 <= element < 30 + else + ((element % 10)+1) * 1_000_000 + for element in range(0, 40)) + +TEN_PART: int = 0 +THOUSAND_PART: int = 10 +TEN_THOUSAND_PART: int = 20 +MILLION_PART: int = 30 + + +def main(use_tri: Function): + """main function call two other fonction + and take on parameter the sort we want use""" + write_result(do_function(use_tri), use_tri) + + +def do_function(use_tri: Function) -> list[float]: + """do_function make ten time the right sort + and return a tab of time different for the number of element""" + timer_tab: list[float] = [] + for element in range(len(LOOP_REQUIRED)//4): + tab_generate = generate_array_of_number( + generate_index_tab_with_sort(use_tri, element)) + + start: float = time.time() + # Generate a tab from 1_000_000 to 10_000_000 + if (use_tri == Function.GENERATE_ARRAY): + generate_array_of_number(LOOP_REQUIRED[element + MILLION_PART]) + + # Use factorial on number from 10 to 100 + if (use_tri == Function.FACTORIAL): + get_factorial(LOOP_REQUIRED[element//3] // 100) + + # Use selection sort on tab from 1_000 to 10_000 + if (use_tri == Function.SELECTION_SORT): + selection_sort(tab_generate) + + # Use insertion sort on tab from 1_000 to 10_000 + if (use_tri == Function.INSERTION_SORT): + insertion_sort(tab_generate) + + # Use fusion sort on tab from 10_000 to 100_000 + if (use_tri == Function.FUSION_SORT): + fusion_sort(tab_generate) + + # Use python sort on tab from 1_000_000 to 10_000_000 + if (use_tri == Function.PYTHON_SORT): + tab_generate.sort() + + end: float = time.time() + timer_tab.append(end-start) + + return timer_tab + + +def generate_index_tab_with_sort(use_tri: Function, element: int) -> int: + """Generate a tab with different length for different sort""" + + # Generate a tab from 10 to 100 + if (use_tri == Function.FACTORIAL): + return LOOP_REQUIRED[element + TEN_PART] + + # Generate a tab from 10_000 to 100_000 + elif (use_tri == Function.FUSION_SORT): + return LOOP_REQUIRED[element + TEN_THOUSAND_PART] + + # Generate a tab from 1_000_000 to 10_000_000 + elif (use_tri == Function.PYTHON_SORT): + return LOOP_REQUIRED[element + MILLION_PART] + + # Generate a tab from 1_000 to 10_000 + else: + return LOOP_REQUIRED[element + THOUSAND_PART] + + +def write_result(timer_tab: list[float], use_tri: Function) -> None: + """write_result take tab of time and the sort for write them to user""" + + for element in range(len(timer_tab)): + tab_generate = generate_index_tab_with_sort(use_tri, element) + # print time for function use + print("Le résultat pour " + str(tab_generate) + + " entrées est " + str(timer_tab[element])) + + +# A décommenter +# main(Function.GENERATE_ARRAY) +# main(Function.SELECTION_SORT) +# main(Function.INSERTION_SORT) +# main(Function.FACTORIAL) +# main(Function.FUSION_SORT) +# main(Function.PYTHON_SORT) + + +""" +Le tri sort de python est environ 100 fois plus rapide que notre tri fusion. +Sa complexité semble etre N + +Résultat du python sort : +Le résultat pour 1000000 entrées est 0.10117459297180176 +Le résultat pour 2000000 entrées est 0.24679017066955566 +Le résultat pour 3000000 entrées est 0.31505799293518066 +Le résultat pour 4000000 entrées est 0.42998576164245605 +Le résultat pour 5000000 entrées est 0.537330150604248 +Le résultat pour 6000000 entrées est 0.6411576271057129 +Le résultat pour 7000000 entrées est 0.7325379848480225 +Le résultat pour 8000000 entrées est 0.9010059833526611 +Le résultat pour 9000000 entrées est 0.9867992401123047 +Le résultat pour 10000000 entrées est 1.0364351272583008 +""" diff --git a/graphique courbes.xlsx b/graphique courbes.xlsx new file mode 100644 index 0000000..840f936 Binary files /dev/null and b/graphique courbes.xlsx differ diff --git a/sort/fusion.py b/sort/fusion.py index 73a21d3..ba67867 100644 --- a/sort/fusion.py +++ b/sort/fusion.py @@ -1,2 +1,73 @@ +""" +Le tri fusion prend un tableau. +Si le tableau n'est pas vide ou n'est pas composé d'un seul élément, +Python vas le divisé en deux en rappelant la meme fonction puis le merger. +A chaque fois avant de merger les différents tableaux vont se réduire, +jusqu'à ce que chaque tableau n'ait qu'un élément. +Ensuite les tableaux vont se merger petit à petit en restant croissant et +en formant au final un seul tableau +""" + + def sort(array: list[int]) -> list[int]: - return array + """if length of array <=1 return array, + else return the merge of the left part and the right part of the array""" + if len(array) <= 1: + return array + return merge(sort(array[:len(array) // 2]), sort(array[len(array) // 2:])) + + +def merge(array_left: list[int], array_right: list[int]) -> list[int]: + """We can erge two array together by forming a new array""" + """merge take two array and create one new array we both of them, + at every loop it takes the smallest number between two arrays + and remove from the array. Then we return new array created""" + new_array: list[int] = [] + + # We loop until the new array have all left and right values + while (True): + + # if left or right list is empty then we can had the other + # at our new array and return it because it is sorted + if (array_left == []): + return new_array + array_right + elif (array_right == []): + return new_array + array_left + + # if we suppose that the right and the left list is sorted then for + # have a new sort we just need to compare the first element of each + # list. We don't forget to add it at the new array and remove it + # because we add this value + if array_right[0] < array_left[0]: + new_array.append(array_right.pop(0)) + else: + new_array.append(array_left.pop(0)) + + +""" +Résultat : +Le résultat pour 10000 entrées est 0.14179372787475586 +Le résultat pour 20000 entrées est 0.11987948417663574 +Le résultat pour 30000 entrées est 0.18050265312194824 +Le résultat pour 40000 entrées est 0.32453370094299316 +Le résultat pour 50000 entrées est 0.38787841796875 +Le résultat pour 60000 entrées est 0.49108314514160156 +Le résultat pour 70000 entrées est 0.6442813873291016 +Le résultat pour 80000 entrées est 0.9117996692657471 +Le résultat pour 90000 entrées est 0.9976096153259277 +Le résultat pour 100000 entrées est 1.2517235279083252 + +Le tri fusion a l'air d'etre de complexité N*log(N) +car on subdivise le tri en plusieurs tri de 1 élément. +Puis on les recombine ensemble en les lisant. +Le nombre d'étape ou on lit chaque valeur est +a chaque fois que l'on merge des élements (2**k)>=n>(2**(k-1))+1. +Il a donc k étapes ou on lit tout n. k est plus petit que N +car on prend le k supérieur ou égal à N + +Si on considère que le tri par fusion a une complexité inférieur aux autres tri +alors plus le nombre de valeur va augmenté +plus l'écart entre les différentes complexités va augmenté également. +Le tri sera toujours plus rapide qu'un tri avec une complexité supérieur + +""" diff --git a/sort/insertion.py b/sort/insertion.py index 73a21d3..f6f7d85 100644 --- a/sort/insertion.py +++ b/sort/insertion.py @@ -1,2 +1,52 @@ +""" +Selon moi, Le tri par insertion va regarder chaque élément de la liste, +les uns après les autres. A chaque fois, l'élément sera inséré +entre deux autres éléments dans la liste déja trié, +à la fin ou au début de la liste. On décalera tous les éléments triés +après la nouvelle position, de cette nouvelle position +vers l'ancienne position de l'élément +""" + + def sort(array: list[int]) -> list[int]: + """we sort a list of int by insert each number at their position""" + + # The first loop represent element who search to sort + # and who will be insert between other number + for increment in range(len(array)): + + # The secund loop go from the element who want to sort + # to the begin of the list + for number in range(increment, -1, -1): + + # if the element size is between two element or + # if we reach the top of the list we insert it on list + if (array[increment] < array[number] + and (array[increment] >= array[number-1] + or number == 0)): + + # We remove the element at increment + # position and we insert it at number position + array.insert(number, array.pop(increment)) return array + + +""" +Résultat : +Le résultat pour 1000 entrées est 0.028821706771850586 +Le résultat pour 2000 entrées est 0.16448330879211426 +Le résultat pour 3000 entrées est 0.3654301166534424 +Le résultat pour 4000 entrées est 0.691014289855957 +Le résultat pour 5000 entrées est 1.0119526386260986 +Le résultat pour 6000 entrées est 1.4506659507751465 +Le résultat pour 7000 entrées est 2.0582830905914307 +Le résultat pour 8000 entrées est 2.850092887878418 +Le résultat pour 9000 entrées est 3.4401211738586426 +Le résultat pour 10000 entrées est 4.12743878364563 + +Le tri par insertion est de complexité O(N**2) +Car entre la première et la dernière valeur les entrées sont multiplé par 10 +alors que les temps sont multipliés par 100. +De plus on a directement une double boucle dans le programme +qui regarde tout les éléments, ce qui fait N*N donc N**2 +""" diff --git a/sort/range.py b/sort/range.py index fc252ab..34633da 100644 --- a/sort/range.py +++ b/sort/range.py @@ -1,2 +1,21 @@ +import random + + def generate_array_of_number(array_size: int) -> list[int]: - return [] + """ return an array generate number randomly between 1 and 100""" + return [random.randint(0, 100) for i in range(array_size)] + + +""" +Résultat : +Le résultat pour 1000000 entrées est 0.4899604320526123 +Le résultat pour 2000000 entrées est 0.9306461811065674 +Le résultat pour 3000000 entrées est 1.527371883392334 +Le résultat pour 4000000 entrées est 1.882685899734497 +Le résultat pour 5000000 entrées est 2.3193047046661377 +Le résultat pour 6000000 entrées est 3.0640199184417725 +Le résultat pour 7000000 entrées est 3.450831651687622 +Le résultat pour 8000000 entrées est 3.9937903881073 +Le résultat pour 9000000 entrées est 4.156933307647705 +Le résultat pour 10000000 entrées est 4.718446493148804 +""" diff --git a/sort/recursion.py b/sort/recursion.py index e7f4320..714f21a 100644 --- a/sort/recursion.py +++ b/sort/recursion.py @@ -1,2 +1,18 @@ def get_factorial(number: int) -> int: - return number + """ return the factorial of the number""" + return number * get_factorial(number - 1) if number >= 1 else 1 + + +""" +Résultat : +Le résultat pour 10 entrées est 0.0 +Le résultat pour 20 entrées est 0.0 +Le résultat pour 30 entrées est 0.0 +Le résultat pour 40 entrées est 0.0 +Le résultat pour 50 entrées est 0.0 +Le résultat pour 60 entrées est 0.0 +Le résultat pour 70 entrées est 0.0 +Le résultat pour 80 entrées est 0.0 +Le résultat pour 90 entrées est 0.0 +Le résultat pour 100 entrées est 0.0 +""" diff --git a/sort/selection.py b/sort/selection.py index 73a21d3..b6a5b23 100644 --- a/sort/selection.py +++ b/sort/selection.py @@ -1,2 +1,53 @@ +""" +Selon moi le tri par selection va regarder toute la liste, +il va selectionner l'element le plus petit et le placer au début de la liste. +Ensuite, il recommence avec le reste de la liste tant qu'elle n'est pas vide. +""" + + def sort(array: list[int]) -> list[int]: + """We sort a list of int by select the minimum number + each time for build the list""" + + # The first loop can permit us to search the minimum of the list then + # we sort it and we search again the minimum until we reach end of list + for increment in range(len(array)): + + # We define the minimum at the increment index + index_min: int = increment + + # The second loop check all number between the index "increment" and + # the end of the list. Number before increment index are already sorted + for number in range(increment, len(array)): + + # We modify index of the minimum if we find a number smaller. + # Each turn we search the minimum of rest of the list + # that we weren't sort + if array[index_min] > array[number]: + index_min = number + + # We permut the minimum we find with the increment who represent + # the begin of the list we already search + array[increment], array[index_min] = array[index_min], array[increment] return array + + +""" +Résultat : +Le résultat pour 1000 entrées est 0.027270793914794922 +Le résultat pour 2000 entrées est 0.10175871849060059 +Le résultat pour 3000 entrées est 0.2276897430419922 +Le résultat pour 4000 entrées est 0.41335439682006836 +Le résultat pour 5000 entrées est 0.7159967422485352 +Le résultat pour 6000 entrées est 0.9541590213775635 +Le résultat pour 7000 entrées est 1.2389578819274902 +Le résultat pour 8000 entrées est 1.6056084632873535 +Le résultat pour 9000 entrées est 2.092197895050049 +Le résultat pour 10000 entrées est 2.540802001953125 + +Le tri par selection est de complexité O(N**2) +Car entre la première et la dernière valeur les entrées sont multiplé par 10 +alors que les temps sont multipliés par 100. +De plus on a directement une double boucle dans le programme +qui regarde tout les éléments, ce qui fait N*N donc N**2 +"""