Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
90 changes: 78 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,24 @@ Complétez la fonction `generate_array_of_number` du fichier `sort/range.py` pou
Mesurez combien de temps prend python à générer un tableau composés de :

- 1 000 000 entrées
= Temps écoulé : 0.5889501571655273
- 2 000 000 entrées
= Temps écoulé : 1.1459660530090332
- 3 000 000 entrées
= Temps écoulé : 1.7308893203735352
- ...
- 10 000 000 entrées
= Temps écoulé : 5.838363170623779

<img src="img/g1.png">

**Astuce** : vous pouvez écrire les nombres avec des underscores pour mieux les lire : `1_000_000`

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 ?

==> Un algorithme de complexité O(n)

<img src="img/o.webp" width="400">

Expand All @@ -75,28 +83,42 @@ Observez attentivement l'animation de tri par sélection ci-dessous pour en comp

<img src="img/selection.gif">

É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 a deux valeurs qui se comparent entre elles dans le tableau. Les deux le parcourent progressivement, une case à la fois. Quand la première valeur (en bleu) est plus grande que la seconde (en rouge) on stoppe la progression de la valeur rouge. Elle reprend et saute à la position derrière la valeur bleue dès que cette dernière devient plus petite. Quand la première valeur (en bleu) atteint la fin du tableau, on inverse la deuxième valeur en rouge avec la dernière valeur triée. On répète ce processus jusqu'à la fin du tableau.

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 :

- 1000 entrées
- 2000 entrées
- ...
- 10000 entrées
Temps écoulé : 0.04999995231628418
Temps écoulé : 0.2057938575744629
Temps écoulé : 0.46288228034973145
Temps écoulé : 0.8257730007171631
Temps écoulé : 1.2953431606292725
Temps écoulé : 1.8596084117889404
Temps écoulé : 2.533503532409668
Temps écoulé : 3.2937428951263428
Temps écoulé : 4.1926844120025635
Temps écoulé : 5.192129850387573

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*
<img src="img/g2.png">

Quelle semble être la complexité de notre fonction de tri ? Cela est-il logique par rapport au code que vous avez implémenté ?

==> complexité O(n²), la durée du processus (et la complexité) est décuplée du fait de la boucle imbriquée

### 2. Tri par insertion

Observez attentivement l'animation de tri par insertion ci-dessous pour en comprendre le fonctionnement.

<img src="img/insertion.gif">

É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 ?

Dans un tableau à entiers mélangés, on va progressivement le parcourir en placant chaque entier plus ou moins en arrière ou pas en fonction de sa grandeur par rapport aux précédents entiers parcourus. On commence à l'index 1 et on continue jusqu'à la fin du tableau. On compare le dernier élément de la portion déjà triée/parcourue avec le suivant, si le suivant est plus petit, on va le placer dans la bonne position dans la portion déjà parcourue.

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.

Expand All @@ -106,10 +128,24 @@ Mesurez le temps d'éxécution pour un tableau de :
- 2000 entrées
- ...
- 10000 entrées
Temps écoulé : 0.037030696868896484
Temps écoulé : 0.1215670108795166
Temps écoulé : 0.2595536708831787
Temps écoulé : 0.4952089786529541
Temps écoulé : 0.7450480461120605
Temps écoulé : 1.0163190364837646
Temps écoulé : 1.4117050170898438
Temps écoulé : 1.8339478969573975
Temps écoulé : 2.30259108543396
Temps écoulé : 2.8572349548339844

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*
<img src="img/g3.png">

Quelle semble être la complexité de notre fonction de tri ? Cela est-il logique par rapport au code que vous avez implémenté ?

==> complexité O(n²), la durée du processus (et la complexité) est décuplée du fait de la boucle imbriquée

### 3. Tri par fusion

Expand Down Expand Up @@ -154,7 +190,9 @@ 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 ?

Dans un tableau à entiers mélangés, on va le diviser par deux plusieurs fois de manière à avoir plusieurs groupes jusqu'à ce qu'il ne reste que plusieurs petits morceaux de 1 entier. On va ensuite prendre par groupe de deux chaque petit morceau et trier, puis regrouper les groupes de deux en quatre, et re-trier, puis regrouper en huit, et ainsi de suite. À la fin, on se retrouve avec le tableau d'origine reconstitué et trié.

Complétez la fonction `sort` du fichier `sort/fusion.py` en suivant les instructions suivantes.

Expand All @@ -172,18 +210,46 @@ Mesurez le temps d'éxécution pour un tableau de :
- ...
- 10000 entrées

Temps écoulé : 0.010993719100952148
Temps écoulé : 0.020018577575683594
Temps écoulé : 0.032997846603393555
Temps écoulé : 0.019037485122680664
Temps écoulé : 0.023550033569335938
Temps écoulé : 0.02600574493408203
Temps écoulé : 0.031022071838378906
Temps écoulé : 0.03447532653808594
Temps écoulé : 0.04099726676940918
Temps écoulé : 0.0455470085144043

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*
<img src="img/g4.png">

Quelle semble être la complexité de notre fonction de tri ? Cela est-il logique par rapport au code que vous avez implémenté ?

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*
==> complexité O(n*log 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 ?

### 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 ;)

Temps écoulé : 0.0010001659393310547
Temps écoulé : 0.001971006393432617
Temps écoulé : 0.0030002593994140625
Temps écoulé : 0.0030336380004882812
Temps écoulé : 0.003978252410888672
Temps écoulé : 0.0049991607666015625
Temps écoulé : 0.00602269172668457
Temps écoulé : 0.005975008010864258
Temps écoulé : 0.007005453109741211
Temps écoulé : 0.007962942123413086

La durée du processus est en effet inférieure à 0.01 seconde dans la plupart des cas.

## Pour rendre ce TP

Merci de faire une Pull Request vers ce repository.
Expand Down
8 changes: 6 additions & 2 deletions __main__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
def main():
print("Hello world")
from sort.range import generate_array_of_number
from sort.selection import sort

def main():
arr = generate_array_of_number(100)
sortarr = sort(arr)
print(sortarr)

main()
Binary file added img/g1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/g2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/g3.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added img/g4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 9 additions & 0 deletions sort/count.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import time

def count_time():
"""Count time:
Simple method to count time between the start and end of an operation"""
start: float = time.time()
# do something
end: float = time.time()
print("Temps écoulé :", end - start)
42 changes: 41 additions & 1 deletion sort/fusion.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,42 @@
def sort(array: list[int]) -> list[int]:
return array
"""Merge Sort Part 1:
Continuously (recursively) split the given array in two,
the result is merged and returned"""
length = len(array)

if length <= 1:
return array

# Split the array in two and sort each
middle = length // 2
left = sort(array[:middle])
right = sort(array[middle:])

# Merge the two sorted halves
return merge(left, right)


def merge(left, right):
"""Merge Sort Part 2:
Sort and merge two given halveds of an array"""
merged_list = []
left_i = 0
right_i = 0

# Merge the two lists while there are elements in both
while left_i < len(left) and right_i < len(right):
if left[left_i] < right[right_i]:
# Place left element first as it's less than right element
merged_list.append(left[left_i])
left_i += 1
else:
# Place right element first as it's less than left element
merged_list.append(right[right_i])
right_i += 1

# Takes the remaining elements in the slice
# and appends them to the end of merged_list (they're already sorted)
merged_list.extend(left[left_i:])
merged_list.extend(right[right_i:])

return merged_list
24 changes: 24 additions & 0 deletions sort/graph.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import matplotlib.pyplot as plt

x_values = [1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000]
y_values = [
0.010993719100952148,
0.020018577575683594,
0.032997846603393555,
0.019037485122680664,
0.023550033569335938,
0.02600574493408203,
0.031022071838378906,
0.03447532653808594,
0.04099726676940918,
0.0455470085144043,
]

plt.plot(x_values, y_values, marker='o', linestyle='-', color='r')


plt.xlabel('Array Entries')
plt.ylabel('Time of execution (seconds)')
plt.title('Y Execution Time of X elements in Array')

plt.show()
25 changes: 25 additions & 0 deletions sort/insertion.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,27 @@
def sort(array: list[int]) -> list[int]:
"""
Insertion Sort:
Sort an array one element at a time by iteratively shift elements to their correct positions
in the array
"""

# Determine the length of the array
length = len(array)

# The loop is starting from the second element, index 1
for i in range(1, length):
# The current element we're comparing against the last sorted element
current_element = array[i]

# Last sorted item idx needed to find the position where we need to put current element
last_sorted_index = i-1

# Shift elements of the sorted portion to the right if they are larger than current element
while last_sorted_index >= 0 and array[last_sorted_index] > current_element:
array[last_sorted_index+1] = array[last_sorted_index]
last_sorted_index -= 1

# Place current element into its correct position
array[last_sorted_index + 1] = current_element

return array
7 changes: 6 additions & 1 deletion sort/range.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
import random

def generate_array_of_number(array_size: int) -> list[int]:
return []
"""Generate a random list of numbers
"""
array = [random.randint(0, 100) for i in range(array_size)]
return array
3 changes: 2 additions & 1 deletion sort/recursion.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
def get_factorial(number: int) -> int:
return number
"""Get the factorial of a number using recursion"""
return get_factorial(number-1) * number if number > 1 else 1
30 changes: 30 additions & 0 deletions sort/selection.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,32 @@
def sort(array: list[int]) -> list[int]:
"""Selection Sort:
Continuously explore a given list, compare adjacent elements and swaps them
if they are in the wrong order
"""

# Determine the length of the array
length = len(array)

# Do length-1 turns through the array (breaks if finished = False)
for turn in range(length - 1):
# Flag to check if any swap has been made in this turn
# (=> Iteration with no swap means that every element has been checked
# and thus the array has every number in order)
swap = False

# Traverse the array from 0 to length-turn-1
for i in range(length - turn - 1):
i1 = i
i2 = i + 1
# Compare the current element with the next one
if array[i1] > array[i2]:
# Swap them if the current element is greater than the next one
array[i1], array[i2] = array[i2], array[i1]
# A swap was made
swap = True

# If no swaps were made, the array is sorted
if not swap:
break

return array