From 72fa52f1cfc029ad1f953f140a151160a0a3f947 Mon Sep 17 00:00:00 2001 From: Kayyissa Date: Fri, 24 Nov 2023 15:44:54 +0100 Subject: [PATCH 1/4] tout sauf fusion et relecture --- README.md | 15 +++++++++------ __main__.py | 16 +++++++++++++++- sort/fusion.py | 31 +++++++++++++++++++++++++++++++ sort/insertion.py | 13 +++++++++++++ sort/range.py | 5 ++++- sort/recursion.py | 5 +++++ sort/selection.py | 19 +++++++++++++++++++ 7 files changed, 96 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 74f925a..b9f4621 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ 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* +Comment vous semble évoluer la courbe ? Observez bien les différentes courbes du graphique ci-dessous. Quelle est la plus ressemblante à notre situation ? O(n) (Par ressemblance visuelle avec le graphique fourni, O(n) et O(n log n) sont acceptables. Il faut l'explication des complexités, ou un graphique exposant davantage la courbe de n log n pour répondre à la question.) @@ -75,7 +75,8 @@ 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 ? +On explore un tableau à la recherche de la plus petite valeur, puis on la place au premier emplacement disponible. À la première itération, la plus petite valeur du tableau est donc placée à l'index 0. À la seconde, la valeur la plus petite trouvée entre l'index 1 et la fin du tableau est placée à l'index 1, etc. 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 +89,7 @@ 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é ? O(n²). Cela semble logique car pour chaque entrée supplémentaire, à chaque itération (il y en a n * n-index soit presque n²) on explore une valeur de plus. Soit n² fois plus de valeurs. ### 2. Tri par insertion @@ -96,7 +97,8 @@ 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 ? +On regarde pour chaque valeur si elle est plus petite que celle qui la précède. Si c'est le cas, elle est placée avant elle. Cela est répété jusqu'à ce que la valeur précédente soit plus petite que celle qu'on déplace. 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 +111,7 @@ 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é ? O(n²). En effet, pour chaque élément dans le tableau, dans le pire des cas on itère entre le début du tableau et l'index de ce nombre. Cela représente presque n² itérations pour explorer tous les éléments. ### 3. Tri par fusion @@ -154,7 +156,8 @@ 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 ? +On sépare un tableau en 2 jusqu'à ce que tous les élements soient isolés. Ensuite, on compare les élements deux à deux. Le plus petit est placé au début d'un nouveau tableau de 2 élémnts. On compare ensuite deux à deux les premières valeurs des tableaux de deux éléments. La plus petite valeur est placé au début d'un tableau de 4 éléments. On compare ensuite la première valeur du tableau de deux éléments avec la deuxième du tableau dont la première valeur a été prise. On place la plus petite de ces valeurs à la suite de la première. On itère jusqu'à avoir reconstituté un unique tableau. Complétez la fonction `sort` du fichier `sort/fusion.py` en suivant les instructions suivantes. diff --git a/__main__.py b/__main__.py index 98a585e..5e0d0d0 100644 --- a/__main__.py +++ b/__main__.py @@ -1,5 +1,19 @@ +from sort.range import generate_array_of_number +from sort.insertion import sort +import time + def main(): - print("Hello world") + array_list: list[list[int]] = [] + array_size: int = 1000 + for i in range (10): + array_list.append(generate_array_of_number(array_size)) + array_size += 1000 + for i in range (10): + start: float = time.time() + sort(array_list[i]) + end: float = time.time() + print(f"Temps écoulé pour {len(array_list[i])} :", end - start) + #sort([5,2,1]) main() diff --git a/sort/fusion.py b/sort/fusion.py index 73a21d3..72afc2f 100644 --- a/sort/fusion.py +++ b/sort/fusion.py @@ -1,2 +1,33 @@ +def merge(array_A: list[int], array_B: list[int]) -> list[int]: + """sort and merge two given array""" + + merged_array: list[int] = [] + + if len(array_A) == 0: + return array_B + + if len(array_B) == 0: + return array_A + + array_of_min: list[int] = array_A + + if array_A[0] > array_B[0]: + array_of_min = array_B + + merged_array.append(array_of_min[0]) + if len(array_of_min) > 1: + for value in (merge(array_of_min[1 : len(array_of_min)], + array_A if array_of_min == array_B else array_B)): + merged_array.append(value) + + return merged_array + + def sort(array: list[int]) -> list[int]: + """split the array until every element is separated""" + + if len(array) > 1: + array = merge(sort(array[0 : len(array)//2]), + sort(array[len(array)//2 : len(array)])) + return array diff --git a/sort/insertion.py b/sort/insertion.py index 73a21d3..f40722d 100644 --- a/sort/insertion.py +++ b/sort/insertion.py @@ -1,2 +1,15 @@ def sort(array: list[int]) -> list[int]: + """sort a given list by comparing values 2 per 2, + placing the lowest value to the left""" + + for index, value in enumerate(array): + for i in range(1, len(array[:index])+1): + #Can't compare a value with nothing before + if index == 0: + break + if value < array[index-i]: + array[index-i], array[index-i+1] = value, array[index-i] + else: + break + return array diff --git a/sort/range.py b/sort/range.py index fc252ab..fd551ec 100644 --- a/sort/range.py +++ b/sort/range.py @@ -1,2 +1,5 @@ +import random + def generate_array_of_number(array_size: int) -> list[int]: - return [] + """Return an array of number of the given size""" + return [random.randint(0, 100) for i in range(array_size)] diff --git a/sort/recursion.py b/sort/recursion.py index e7f4320..bcdf4b6 100644 --- a/sort/recursion.py +++ b/sort/recursion.py @@ -1,2 +1,7 @@ def get_factorial(number: int) -> int: + """Get the factorial of a number using recursion""" + + if number != 1: + number = number * get_factorial(number - 1) + return number diff --git a/sort/selection.py b/sort/selection.py index 73a21d3..b28ca77 100644 --- a/sort/selection.py +++ b/sort/selection.py @@ -1,2 +1,21 @@ def sort(array: list[int]) -> list[int]: + """sort a given list by searching the lowest value again and again""" + + current_index: int = 0 + lowest_value_index: int = 0 + lowest_value: int + + while current_index < len(array): + lowest_value = array[current_index] + for index, value in enumerate(array[current_index:]): + if value < lowest_value: + lowest_value = value + lowest_value_index = index + current_index + + array[current_index], array[lowest_value_index] = \ + lowest_value, array[current_index] + current_index += 1 + lowest_value_index = current_index + + return array From c12442d8a19e8f7e84d8ec028bb84d061490dbd4 Mon Sep 17 00:00:00 2001 From: Kayyissa Date: Fri, 24 Nov 2023 17:01:42 +0100 Subject: [PATCH 2/4] fusion working yahou --- __main__.py | 9 +++++---- sort/fusion.py | 30 +++++++++++++++--------------- sort/insertion.py | 2 +- sort/range.py | 1 + sort/selection.py | 1 - 5 files changed, 22 insertions(+), 21 deletions(-) diff --git a/__main__.py b/__main__.py index 5e0d0d0..d78bf2a 100644 --- a/__main__.py +++ b/__main__.py @@ -1,19 +1,20 @@ from sort.range import generate_array_of_number -from sort.insertion import sort +from sort.fusion import sort import time + def main(): array_list: list[list[int]] = [] array_size: int = 1000 - for i in range (10): + for i in range(10): array_list.append(generate_array_of_number(array_size)) array_size += 1000 - for i in range (10): + for i in range(10): start: float = time.time() sort(array_list[i]) end: float = time.time() print(f"Temps écoulé pour {len(array_list[i])} :", end - start) - #sort([5,2,1]) + main() diff --git a/sort/fusion.py b/sort/fusion.py index 72afc2f..18a2daa 100644 --- a/sort/fusion.py +++ b/sort/fusion.py @@ -3,22 +3,22 @@ def merge(array_A: list[int], array_B: list[int]) -> list[int]: merged_array: list[int] = [] - if len(array_A) == 0: - return array_B + while len(array_A) != 0 or len(array_B) != 0: - if len(array_B) == 0: - return array_A + if len(array_A) == 0: + merged_array.extend(array_B) + break - array_of_min: list[int] = array_A + if len(array_B) == 0: + merged_array.extend(array_A) + break - if array_A[0] > array_B[0]: - array_of_min = array_B - - merged_array.append(array_of_min[0]) - if len(array_of_min) > 1: - for value in (merge(array_of_min[1 : len(array_of_min)], - array_A if array_of_min == array_B else array_B)): - merged_array.append(value) + if array_A[0] < array_B[0]: + merged_array.append(array_A[0]) + array_A.pop(0) + else: + merged_array.append(array_B[0]) + array_B.pop(0) return merged_array @@ -27,7 +27,7 @@ def sort(array: list[int]) -> list[int]: """split the array until every element is separated""" if len(array) > 1: - array = merge(sort(array[0 : len(array)//2]), - sort(array[len(array)//2 : len(array)])) + array = merge(sort(array[:len(array)//2]), + sort(array[len(array)//2:])) return array diff --git a/sort/insertion.py b/sort/insertion.py index f40722d..485b35f 100644 --- a/sort/insertion.py +++ b/sort/insertion.py @@ -4,7 +4,7 @@ def sort(array: list[int]) -> list[int]: for index, value in enumerate(array): for i in range(1, len(array[:index])+1): - #Can't compare a value with nothing before + # Can't compare a value with nothing before if index == 0: break if value < array[index-i]: diff --git a/sort/range.py b/sort/range.py index fd551ec..c0c62a3 100644 --- a/sort/range.py +++ b/sort/range.py @@ -1,5 +1,6 @@ import random + def generate_array_of_number(array_size: int) -> list[int]: """Return an array of number of the given size""" return [random.randint(0, 100) for i in range(array_size)] diff --git a/sort/selection.py b/sort/selection.py index b28ca77..6749d34 100644 --- a/sort/selection.py +++ b/sort/selection.py @@ -17,5 +17,4 @@ def sort(array: list[int]) -> list[int]: current_index += 1 lowest_value_index = current_index - return array From acb201659605b9fb1f041e4ce424e2f40dc16024 Mon Sep 17 00:00:00 2001 From: Kayyissa Date: Sat, 25 Nov 2023 15:18:40 +0100 Subject: [PATCH 3/4] relectures et graphs --- README.md | 11 +++++++++-- __main__.py | 28 +++++++++++++++++++++++--- img/fusionGraph.png | Bin 0 -> 19156 bytes img/insertionGraph.png | Bin 0 -> 18752 bytes img/selectionGraph.png | Bin 0 -> 17199 bytes sort/fusion.py | 44 ++++++++++++++++++++--------------------- sort/range.py | 3 ++- sort/selection.py | 10 +++++----- 8 files changed, 63 insertions(+), 33 deletions(-) create mode 100644 img/fusionGraph.png create mode 100644 img/insertionGraph.png create mode 100644 img/selectionGraph.png diff --git a/README.md b/README.md index b9f4621..b034fbe 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,8 @@ 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é ? O(n²). Cela semble logique car pour chaque entrée supplémentaire, à chaque itération (il y en a n * n-index soit presque n²) on explore une valeur de plus. Soit n² fois plus de valeurs. ### 2. Tri par insertion @@ -111,6 +113,8 @@ 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é ? O(n²). En effet, pour chaque élément dans le tableau, dans le pire des cas on itère entre le début du tableau et l'index de ce nombre. Cela représente presque n² itérations pour explorer tous les éléments. ### 3. Tri par fusion @@ -177,15 +181,18 @@ 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é ? O(n log n). Cet algorithme utilise la récursivité plutôt que des boucles imbriquées. Pour chaque élement supplémentaire, on va réaliser une récursion de plus, mais pas explorer de nouveau tout le tableau. C'est donc une complexité logique. -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* +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 ? Des tailles je ne sais pas, mais si le tableau fourni est déjà trié, il est plus lent que les autres. ### 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 ;) +Il est effectivement beaucoup plus rapide ! ## Pour rendre ce TP diff --git a/__main__.py b/__main__.py index d78bf2a..ae61709 100644 --- a/__main__.py +++ b/__main__.py @@ -1,20 +1,42 @@ from sort.range import generate_array_of_number -from sort.fusion import sort +from sort.insertion import sort as insersion_sort +from sort.selection import sort as selection_sort +from sort.fusion import sort as fusion_sort + import time def main(): array_list: list[list[int]] = [] array_size: int = 1000 + for i in range(10): array_list.append(generate_array_of_number(array_size)) array_size += 1000 for i in range(10): start: float = time.time() - sort(array_list[i]) + selection_sort(array_list[i]) + end: float = time.time() + print(f"Time for {len(array_list[i])} (selection):", end - start) + + for i in range(10): + start: float = time.time() + insersion_sort(array_list[i]) + end: float = time.time() + print(f"Time for {len(array_list[i])} (insertion):", end - start) + + for i in range(10): + start: float = time.time() + fusion_sort(array_list[i]) + end: float = time.time() + print(f"Time for {len(array_list[i])} (fusion):", end - start) + + for i in range(10): + start: float = time.time() + array_list[i].sort() end: float = time.time() - print(f"Temps écoulé pour {len(array_list[i])} :", end - start) + print(f"Time for {len(array_list[i])} (python sort):", end - start) main() diff --git a/img/fusionGraph.png b/img/fusionGraph.png new file mode 100644 index 0000000000000000000000000000000000000000..d308297f414b132c9c44d2f50168d7a4088a54dc GIT binary patch literal 19156 zcmdtKc|6qH8#u1*s@tNXBwL%UL}n~yZIde_NhP~1Z~IIcj9n$kUdA?KD~c9Nj3s-r zGeZo<7(-b`7{oC4@qN$W-p`-EzkaXp>w8{qU1!dDp65LKd7d+c+`gr?|L@~}b8&I) z*Z$}F9WJh2P%f_RJ-c^+Gx73q5b&>U$U9nBxpG>C#=u{8+FjAT!o`&rwRiR5E^wvx zZP;DSo}L~Oi9{xodwYAqKmQ%qAJ<-Yt-0DNd>U{2G~V&4zvJ6v;M;J|zv+HJ^Zh5yh5-$RPnsV*X*4o2GI`cu z64+w$9B=v@Z}z;&92_t3=D~Q&7mb#|goiH)*4EZQc$*#g6Vc`Xj_@YuD7?$-b~req znjfKmJc{W+f+M;G8AtSpZS{!n@B~L3-V4*^h3)c9?DWGB{K1jZ?Vm~tNFhB*`tdZG z@a#=@U|?X-+xDPza&S87<;$0$>8)Xzl<*Hd5fKrQ?^~lj5MO2WMMp=YKeVAg5#zq} z$LIAYfFtWiB5oil=SLE*FR9>XQsK|!9FHKgK(p|9UNOPa?@ z@ndD+XrNTobyc>GS5;Mg`}Xa7Q+Lge(OM$C9vq#M4R}&R*JM-ISaW-SGij;?96v~S zaP-jeWv`J?YoJM~XzcTZ;@y{oIM8yr7p+4AlC1zRd3}BT=KCifK_%PmcITGCKbD?x1EUM9EMK!QaBk0|e~ge^T##?< z|F%im7*n{o{^8QTe&w#G`6M+eK*$7}Fu(08#E=BLdQkd6_47`dNXxu$y$_yPpRyV} zaW(zOjoMq`mVp(uRxZb-FB?AHb#m}f580Xi<=`opz0L5pqft=E@pI7Vt^k42yA@q# zUGAS&K6Fvt$0tWumOM@UKDleTxNCeGn6H>#OIhIJ60I7YUg{r^MRPs5;y=A$vz_Zg zf(szXb>SFcCs%+xFV|{2jO&jUblW%4|EDId|L&K$-u~Uhi|cMUU}a+*re8f+f)`5B z2KWfcO{d=&%k!Sb9XHI^(g6-kUR;_MYPrUlL)T_=6Q0X}pMw9_@ER}FpS0HY1s-|% z2P_=c*H{R&rr^t-Q@xnmP6~mGc$JVlU+>(011blR=QVf&VzL*{;3CvE{(!-@VMAWO z#UyM8ml1O@5@iJ1#OkFhc7VOChFby2)C}vC1^18HHsRc+n)~QOJ9#2FqQ38HdS0IF zX@Zvw2++zEePA*J#3&slr%8yOZ*61`m4>(zs4VPzjHt;0Ihz%WJnki_g|U*BUI z=j4iC%Q~*^k^rob>M00meckZgARV>tXB~i=R;T8`>wS3q5A)1Nd94AG{{5w@FO%!8 z2{205NuE=WSALtT>c#k4eB0Fy9M=b}dE6ruh?g!k)O!A(bg(@G_Z8uY&CWHSh!{KOnT z=d;J#cFF{^w?V*di+eEC5s75AaWz6;F;9BX)GcA7Dd)x-`A-?&9BwQOb+LSAcYoan zvOVOQf-`jT(_A@hbY31RGklfV@o2PZ93}C!1Mfqv7;D-ckFMFx7O7AMB_gY^z$6gs zWhE-z7+CHL)#%kCXUE?-Co&-v8N)a0LF0Ni9M-e<`Jp%LW5d)szIvUvE0w*SgaB4Q zEI49k#0i>SuT*X2ith@@CaI&jzlnj6!zb_L7KH3b6c{*WU6>h^=L;QDg*OIvW(bFO z$hHPtkyhWSuCa3;nrJ2r%k zU4ZibVX<9N+sNgXLi$Hqp6T|1dcARKB05HX5=y8&1`02HvSWAaj#3ixSET9og~80> zRLfe6L{_oAc>PNbsuu`ggv#9jHQl<*tpvc@5~y=& zn#n^Uu6tlbcFln~7YoMO!0uYY)OA2eOI%@g_11&wr$uM{hK@^+X%H6IgGnw1VvA< zGUE9Zhd;$n0pdDn*H?i0)5pF(Iig8PLY)HXm}93w<;pWV>qL8~it}-az%62hYzeR; z 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 TU%`JMh)Y}Z*7cmLme2kVJ`ZH= literal 0 HcmV?d00001 diff --git a/img/insertionGraph.png b/img/insertionGraph.png new file mode 100644 index 0000000000000000000000000000000000000000..fe94721f0ef19a6cfbe29f47a6730b3f8150c6c4 GIT binary patch literal 18752 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 b~$z3p{O&@IVuU+^PHKGO|~;r8Rv|Qvq#1` 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 list[int]: - """sort and merge two given array""" +def sort(array: list[int]) -> list[int]: + """sort using fusion: + split the given list until every element is separated, + then compare them to merge them into a sorted list""" + + if len(array) > 1: + array = merge(sort(array[:len(array)//2]), + sort(array[len(array)//2:])) + + return array + + +def merge(array_a: list[int], array_b: list[int]) -> list[int]: + """sort and merge two ordered given lists""" merged_array: list[int] = [] - while len(array_A) != 0 or len(array_B) != 0: + while len(array_a) != 0 or len(array_b) != 0: - if len(array_A) == 0: - merged_array.extend(array_B) + if len(array_a) == 0: + merged_array.extend(array_b) break - if len(array_B) == 0: - merged_array.extend(array_A) + if len(array_b) == 0: + merged_array.extend(array_a) break - if array_A[0] < array_B[0]: - merged_array.append(array_A[0]) - array_A.pop(0) + if array_a[0] < array_b[0]: + merged_array.append(array_a.pop(0)) else: - merged_array.append(array_B[0]) - array_B.pop(0) + merged_array.append(array_b.pop(0)) return merged_array - - -def sort(array: list[int]) -> list[int]: - """split the array until every element is separated""" - - if len(array) > 1: - array = merge(sort(array[:len(array)//2]), - sort(array[len(array)//2:])) - - return array diff --git a/sort/range.py b/sort/range.py index c0c62a3..fcd90a1 100644 --- a/sort/range.py +++ b/sort/range.py @@ -2,5 +2,6 @@ def generate_array_of_number(array_size: int) -> list[int]: - """Return an array of number of the given size""" + """Return an array of numbers between 0 and 100 of the given size""" + return [random.randint(0, 100) for i in range(array_size)] diff --git a/sort/selection.py b/sort/selection.py index 6749d34..4059cb7 100644 --- a/sort/selection.py +++ b/sort/selection.py @@ -3,17 +3,17 @@ def sort(array: list[int]) -> list[int]: current_index: int = 0 lowest_value_index: int = 0 - lowest_value: int while current_index < len(array): - lowest_value = array[current_index] + lowest_value_index = current_index + for index, value in enumerate(array[current_index:]): - if value < lowest_value: - lowest_value = value + if value < array[lowest_value_index]: lowest_value_index = index + current_index array[current_index], array[lowest_value_index] = \ - lowest_value, array[current_index] + array[lowest_value_index], array[current_index] + current_index += 1 lowest_value_index = current_index From ca71ab0600077e880833d60025971ceb8b9798c3 Mon Sep 17 00:00:00 2001 From: Kayyissa Date: Sat, 25 Nov 2023 16:58:07 +0100 Subject: [PATCH 4/4] un peu plus prop --- sort/insertion.py | 8 +++++--- sort/recursion.py | 5 +---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/sort/insertion.py b/sort/insertion.py index 485b35f..f7c29cb 100644 --- a/sort/insertion.py +++ b/sort/insertion.py @@ -3,10 +3,12 @@ def sort(array: list[int]) -> list[int]: placing the lowest value to the left""" for index, value in enumerate(array): + + # Can't compare a value with nothing before + if index == 0: + continue + for i in range(1, len(array[:index])+1): - # Can't compare a value with nothing before - if index == 0: - break if value < array[index-i]: array[index-i], array[index-i+1] = value, array[index-i] else: diff --git a/sort/recursion.py b/sort/recursion.py index bcdf4b6..46eec10 100644 --- a/sort/recursion.py +++ b/sort/recursion.py @@ -1,7 +1,4 @@ def get_factorial(number: int) -> int: """Get the factorial of a number using recursion""" - if number != 1: - number = number * get_factorial(number - 1) - - return number + return number * get_factorial(number - 1) if number > 1 else 1