Mini-projets algorithmique, corrigé TP KNN, news, notebooks IA

This commit is contained in:
2026-07-07 10:57:59 +02:00
parent 1647035df2
commit 5a6d602a91
51 changed files with 3614 additions and 33 deletions

Binary file not shown.

View File

@@ -0,0 +1,41 @@
# Projet 01 — Le Nombre Mystère
# Algorithme : Recherche dichotomique
def devine():
"""
L'ordinateur devine le nombre auquel pense l'utilisateur
en utilisant la recherche dichotomique.
"""
print("=== Le Nombre Mystère ===")
print("Pense à un nombre entre 1 et 100 (sans le dire !)")
print("Réponds avec : < (trop grand), > (trop petit), = (trouvé !)")
print()
gauche = 1
droite = 100
nb_essais = 0
while gauche <= droite:
milieu = (gauche + droite) // 2
nb_essais += 1
reponse = input(f"Essai {nb_essais} — Est-ce que ton nombre est {milieu} ? ")
if reponse == "=":
print(f"Trouvé ! C'était bien {milieu}, en {nb_essais} essai(s) !")
return
elif reponse == "<":
# TODO : le nombre cherché est plus petit que milieu
# Que faut-il modifier ? gauche ou droite ?
pass
elif reponse == ">":
# TODO : le nombre cherché est plus grand que milieu
pass
else:
print("Réponse invalide, utilise <, > ou =")
print("Je n'ai pas trouvé... tu as peut-être changé de nombre !")
# --- Programme principal ---
devine()

View File

@@ -0,0 +1,44 @@
# 01 — Le Nombre Mystère
**Algorithme :** Recherche dichotomique
**Difficulté :** Facile
**Durée estimée :** 45 min — 1h
---
## Contexte
Vous pensez à un nombre entre 1 et 100. L'ordinateur doit le deviner le plus vite possible en posant des questions.
À chaque essai, vous répondez :
- `<` si votre nombre est **plus petit** que la proposition
- `>` si votre nombre est **plus grand**
- `=` si c'est **le bon nombre**
L'ordinateur utilise la **recherche dichotomique** : à chaque question, il coupe l'intervalle possible en deux.
---
## Étapes
### Étape 1 — Comprendre la stratégie
Sans coder, répondez sur papier : si le nombre est entre 1 et 100, quelle est la première proposition optimale ? Pourquoi ?
### Étape 2 — Implémenter `devine()`
Complétez la fonction `devine()` dans `starter.py` :
- L'ordinateur part de `gauche = 1` et `droite = 100`
- Il propose `milieu = (gauche + droite) // 2`
- Selon la réponse, il réduit l'intervalle
- Il compte le nombre d'essais
### Étape 3 — Tester
Lancez le programme et jouez plusieurs parties. Vérifiez que l'ordinateur trouve toujours en **7 essais maximum**.
### Étape 4 — Extension (si vous avez le temps)
Ajoutez une deuxième version où **c'est vous** qui devez deviner le nombre de l'ordinateur. L'ordinateur choisit un nombre aléatoire (`random.randint(1, 100)`), et le programme vous dit si vous êtes trop grand ou trop petit. Comptez vos essais et comparez avec le maximum optimal.
---
## Ce que vous devez rendre
- Le programme qui fonctionne
- La réponse à la question : pourquoi 7 essais suffisent toujours pour 100 nombres ?

Binary file not shown.

View File

@@ -0,0 +1,81 @@
# Projet 02 — Le Tri de Cartes
# Algorithme : Tri par insertion
NOMS_VALEURS = {11: "Valet", 12: "Dame", 13: "Roi", 14: "As"}
def nom_carte(carte):
"""Retourne une représentation lisible d'une carte."""
valeur, couleur = carte
nom = NOMS_VALEURS.get(valeur, str(valeur))
return f"{nom} de {couleur}"
def afficher_main(main):
"""Affiche toutes les cartes d'une main."""
print("Main :", " | ".join(nom_carte(c) for c in main))
def tri_insertion_cartes(main):
"""
Trie la main de cartes par valeur croissante (tri par insertion).
Chaque carte est un tuple (valeur, couleur).
On trie selon le premier élément du tuple : la valeur.
"""
for i in range(1, len(main)):
x = main[i]
j = i
# TODO : compléter la condition du while
# Indice : on compare les valeurs (main[j-1][0] et x[0])
while j > 0 and ...:
main[j] = main[j - 1]
j -= 1
main[j] = x
return main
def chercher_carte(main_triee, valeur):
"""
Recherche dichotomique : retourne True si une carte de cette valeur
est dans la main triée, False sinon.
"""
a = 0
b = len(main_triee) - 1
while a <= b:
m = (a + b) // 2
valeur_m = main_triee[m][0] # on compare les valeurs
if valeur_m == valeur:
return True
elif valeur_m < valeur:
# TODO
pass
else:
# TODO
pass
return False
# --- Programme principal ---
main = [
(7, "Coeur"),
(2, "Pique"),
(11, "Trèfle"),
(5, "Carreau"),
(14, "Coeur"),
(9, "Pique"),
(3, "Carreau"),
]
print("=== Le Tri de Cartes ===")
print()
print("Avant le tri :")
afficher_main(main)
tri_insertion_cartes(main)
print()
print("Après le tri par insertion :")
afficher_main(main)
print()
# Test de recherche
for valeur_cherchee in [7, 12, 14, 6]:
resultat = chercher_carte(main, valeur_cherchee)
nom = NOMS_VALEURS.get(valeur_cherchee, str(valeur_cherchee))
print(f"Cherche {nom} : {'trouvé !' if resultat else 'absent'}")

View File

@@ -0,0 +1,39 @@
# 02 — Le Tri de Cartes
**Algorithme :** Tri par insertion
**Difficulté :** Facile
**Durée estimée :** 45 min — 1h
---
## Contexte
Lorsqu'on reçoit des cartes à jouer, on les trie naturellement dans sa main : on prend chaque nouvelle carte et on la glisse à la bonne place parmi celles déjà en ordre. C'est exactement le **tri par insertion**.
Dans ce projet, vous allez trier une main de cartes par valeur croissante, puis chercher une carte avec la dichotomie.
---
## Étapes
### Étape 1 — Afficher la main
Lancez le starter et observez comment les cartes sont représentées. Chaque carte est un **tuple** `(valeur, couleur)`. La valeur va de 2 à 14 (11 = Valet, 12 = Dame, 13 = Roi, 14 = As).
### Étape 2 — Implémenter `tri_insertion_cartes(main)`
Le tri doit classer les cartes par **valeur croissante** (le premier élément du tuple).
> **Indication** : dans le tri par insertion vu en cours, on comparait `tab[j-1] > x`. Ici, vous comparerez `main[j-1][0] > x[0]` (on ne regarde que la valeur, pas la couleur).
### Étape 3 — Chercher une carte
Implémentez `chercher_carte(main_triee, valeur)` qui utilise la **dichotomie** pour savoir si une carte d'une certaine valeur est dans la main.
> **Attention** : la dichotomie ne fonctionne que sur une liste **déjà triée** !
### Étape 4 — Extension (si vous avez le temps)
Ajoutez un mode "nouvelle donne" : générez une main aléatoire de 7 cartes avec `random`, triez-la et affichez-la.
---
## Ce que vous devez rendre
- Le programme qui trie et cherche correctement
- Une explication : pourquoi le tri par insertion ressemble à ce qu'on fait naturellement avec des cartes ?

Binary file not shown.

View File

@@ -0,0 +1,93 @@
# Projet 03 — Le Podium
# Algorithme : Tri par sélection
athletes = [
{"nom": "Marcus", "pays": "JAM", "temps": 9.97},
{"nom": "Alice", "pays": "FRA", "temps": 9.85},
{"nom": "Yusuf", "pays": "TUR", "temps": 10.03},
{"nom": "Carla", "pays": "BRA", "temps": 9.97},
{"nom": "Bob", "pays": "USA", "temps": 9.91},
{"nom": "Lena", "pays": "GER", "temps": 10.11},
{"nom": "Kwame", "pays": "GHA", "temps": 9.99},
{"nom": "Sofia", "pays": "ITA", "temps": 10.07},
]
def tri_selection_athletes(athletes):
"""
Trie la liste d'athlètes par temps croissant (tri par sélection).
Le meilleur (temps le plus bas) sera en premier.
"""
n = len(athletes)
for i in range(n):
mini = i
for j in range(i + 1, n):
# TODO : comparer les temps pour trouver le minimum
if ...:
mini = j
athletes[i], athletes[mini] = athletes[mini], athletes[i]
return athletes
def afficher_podium(athletes):
"""Affiche un podium ASCII avec les 3 premiers athlètes."""
if len(athletes) < 3:
print("Pas assez d'athlètes pour un podium !")
return
or_ = athletes[0]
ar_ = athletes[1]
br_ = athletes[2]
print()
print(" 🥇")
print(" ┌───┐ 🥈 🥉")
print(" │ │ ┌───┐ ┌───┐")
print(" │ 1 │ │ 2 │ │ 3 │")
print(" │ │ │ │ │ │")
print(" └───┘ └───┘ └───┘")
print(f" {or_['nom']:^10} ({or_['pays']}) {ar_['nom']:^10} ({ar_['pays']}) {br_['nom']:^10} ({br_['pays']})")
print(f" {or_['temps']} s {ar_['temps']} s {br_['temps']} s")
print()
def temps_atteint(temps_liste, t):
"""
Recherche dichotomique : retourne True si le temps t
a été réalisé par un athlète, False sinon.
La liste temps_liste doit être triée.
"""
a = 0
b = len(temps_liste) - 1
while a <= b:
m = (a + b) // 2
if temps_liste[m] == t:
return True
elif temps_liste[m] < t:
# TODO
pass
else:
# TODO
pass
return False
# --- Programme principal ---
print("=== Compétition de Sprint ===")
print()
print("Résultats bruts :")
for a in athletes:
print(f" {a['nom']} ({a['pays']}) : {a['temps']} s")
tri_selection_athletes(athletes)
afficher_podium(athletes)
# Classement complet
print("Classement final :")
for i, a in enumerate(athletes, 1):
print(f" {i}. {a['nom']} ({a['pays']}) — {a['temps']} s")
# Recherche dichotomique
temps_liste = [a["temps"] for a in athletes]
print()
for t in [9.85, 10.00, 9.97]:
trouve = temps_atteint(temps_liste, t)
print(f"Temps {t}s réalisé ? {'Oui' if trouve else 'Non'}")

View File

@@ -0,0 +1,49 @@
# 03 — Le Podium
**Algorithme :** Tri par sélection
**Difficulté :** Facile
**Durée estimée :** 45 min — 1h
---
## Contexte
Vous gérez le classement d'une compétition de sprint. Vous devez trier les athlètes par **temps croissant** (le plus rapide en premier) et afficher le podium.
Le tri par sélection fonctionne comme un vrai classement : à chaque étape, on cherche le meilleur parmi tous les restants et on le place en tête.
---
## Étapes
### Étape 1 — Implémenter `tri_selection_athletes(athletes)`
La liste `athletes` contient des dictionnaires `{"nom": ..., "pays": ..., "temps": ...}`.
Il faut trier par `temps` croissant (le plus petit temps = le plus rapide).
> **Indication** : dans le tri par sélection, au lieu de comparer `tab[j] < tab[mini]`, vous comparerez `athletes[j]["temps"] < athletes[mini]["temps"]`.
### Étape 2 — Afficher le podium
Implémentez `afficher_podium(athletes_tries)` qui affiche les 3 premiers avec un podium en ASCII.
```
1er
+---+ 2e 3e
| | +---+ +---+
| 1 | | 2 | | 3 |
| | | | | |
+---+ +---+ +---+
Alice (FR) Bob (USA) Carla (BR)
9.85 s 9.91 s 9.97 s
```
### Étape 3 — Recherche dichotomique
Après le tri, créez une liste des temps uniquement. Implémentez `temps_atteint(temps_liste, t)` qui utilise la dichotomie pour dire si un temps exact a été réalisé lors de la compétition.
### Étape 4 — Extension (si vous avez le temps)
Affichez aussi le classement complet sous forme de tableau numéroté.
---
## Ce que vous devez rendre
- Le programme qui trie et affiche le podium
- Réponse : pourquoi le tri par sélection fait-il **moins de déplacements** que le tri par insertion ?

Binary file not shown.

View File

@@ -0,0 +1,88 @@
# Projet 04 — L'Annuaire
# Algorithmes : Tri par insertion + Recherche dichotomique
# L'annuaire est une liste de dictionnaires, toujours triée par "nom"
annuaire = [
{"nom": "Dupont", "prenom": "Alice", "tel": "06 11 22 33 44"},
{"nom": "Garcia", "prenom": "Carlos", "tel": "07 55 66 77 88"},
{"nom": "Martin", "prenom": "Sophie", "tel": "06 99 88 77 66"},
{"nom": "Petit", "prenom": "Luc", "tel": "07 12 34 56 78"},
{"nom": "Rousseau", "prenom": "Emma", "tel": "06 44 55 66 77"},
]
def afficher_annuaire(annuaire):
"""Affiche tous les contacts sous forme de tableau."""
print(f"\n{'Nom':<15} {'Prénom':<12} {'Téléphone'}")
print("-" * 40)
for contact in annuaire:
print(f"{contact['nom']:<15} {contact['prenom']:<12} {contact['tel']}")
print()
def ajouter_contact(annuaire, contact):
"""
Insère le contact à la bonne place pour maintenir l'ordre alphabétique.
Principe : tri par insertion (un seul élément à insérer).
"""
annuaire.append(contact) # on l'ajoute à la fin temporairement
j = len(annuaire) - 1
# TODO : décaler vers la droite tant que le nom précédent est > au nom du contact
# Indice : comparer annuaire[j-1]["nom"] et contact["nom"]
while j > 0 and ...:
annuaire[j] = annuaire[j - 1]
j -= 1
annuaire[j] = contact
def rechercher(annuaire, nom):
"""
Recherche dichotomique par nom.
Retourne le contact (dict) si trouvé, None sinon.
"""
a = 0
b = len(annuaire) - 1
while a <= b:
m = (a + b) // 2
nom_m = annuaire[m]["nom"]
if nom_m == nom:
return annuaire[m]
elif nom_m < nom:
# TODO
pass
else:
# TODO
pass
return None
def menu():
"""Boucle de menu interactif."""
while True:
print("=== Annuaire ===")
print("1 — Afficher tous les contacts")
print("2 — Ajouter un contact")
print("3 — Rechercher un contact")
print("4 — Quitter")
choix = input("Votre choix : ")
if choix == "1":
afficher_annuaire(annuaire)
elif choix == "2":
nom = input("Nom : ").strip().capitalize()
prenom = input("Prénom : ").strip().capitalize()
tel = input("Téléphone : ").strip()
ajouter_contact(annuaire, {"nom": nom, "prenom": prenom, "tel": tel})
print(f"{nom} ajouté !")
elif choix == "3":
nom = input("Nom à rechercher : ").strip().capitalize()
# TODO : appeler rechercher() et afficher le résultat
pass
elif choix == "4":
print("Au revoir !")
break
else:
print("Choix invalide.")
# --- Programme principal ---
menu()

View File

@@ -0,0 +1,46 @@
# 04 — L'Annuaire
**Algorithmes :** Tri par insertion + Recherche dichotomique
**Difficulté :** Moyen
**Durée estimée :** 1h — 1h15
---
## Contexte
Vous créez un mini-annuaire de contacts. Il doit toujours rester **trié par nom** pour que la recherche dichotomique fonctionne.
Chaque fois qu'on ajoute un contact, on l'insère directement à la bonne place (comme dans le tri par insertion). Chaque fois qu'on cherche, on utilise la dichotomie.
---
## Étapes
### Étape 1 — Afficher l'annuaire
Lancez le starter, vérifiez que `afficher_annuaire()` fonctionne.
### Étape 2 — Implémenter `ajouter_contact(annuaire, contact)`
Cette fonction insère le nouveau contact **à la bonne position** pour maintenir l'ordre alphabétique.
> **Indication** : c'est le principe du tri par insertion, mais appliqué à un seul élément. On parcourt depuis la fin et on décale les contacts dont le nom est alphabétiquement après le nouveau.
>
> Pour comparer des chaînes alphabétiquement en Python : `"Dupont" < "Martin"` → `True`
### Étape 3 — Implémenter `rechercher(annuaire, nom)`
Recherche dichotomique : retourne le contact (dictionnaire complet) si trouvé, `None` sinon.
### Étape 4 — Menu interactif
Complétez la boucle `menu()` pour que l'utilisateur puisse :
- `1` — Afficher tous les contacts
- `2` — Ajouter un contact
- `3` — Rechercher un contact par nom
- `4` — Quitter
### Étape 5 — Extension (si vous avez le temps)
Ajoutez une option `5` pour supprimer un contact par nom (cherchez-le d'abord avec la dichotomie, puis supprimez-le de la liste).
---
## Ce que vous devez rendre
- Le programme avec le menu fonctionnel
- Réponse : dans quel cas la dichotomie est-elle inutilisable directement ?

Binary file not shown.

View File

@@ -0,0 +1,111 @@
# Projet 05 — Le Correcteur Orthographique
# Algorithmes : Tri par insertion + Recherche dichotomique
# Dictionnaire de base, déjà trié alphabétiquement
dictionnaire = [
"algorithme", "annuaire", "arbre", "binaire", "boucle",
"cellule", "chaine", "chat", "chien", "circuit",
"clavier", "code", "complexite", "condition", "donnee",
"ecran", "entier", "fichier", "flottant", "fonction",
"graphe", "indice", "instruction", "langage", "liste",
"logiciel", "memoire", "module", "nombre", "octet",
"ordinateur", "pixel", "programme", "python", "recursion",
"reseau", "serveur", "tableau", "terminal", "variable",
]
def verifier(dictionnaire, mot):
"""
Recherche dichotomique : retourne True si le mot est dans le dictionnaire,
False sinon.
"""
a = 0
b = len(dictionnaire) - 1
while a <= b:
m = (a + b) // 2
if dictionnaire[m] == mot:
return True
elif dictionnaire[m] < mot:
# TODO
pass
else:
# TODO
pass
return False
def verifier_avec_voisins(dictionnaire, mot):
"""
Recherche dichotomique améliorée.
Retourne un tuple (trouve, voisin_avant, voisin_apres) :
- trouve : True si le mot existe, False sinon
- voisin_avant : le mot juste avant dans le dictionnaire (ou None)
- voisin_apres : le mot juste après dans le dictionnaire (ou None)
Indication : à la fin de la boucle, si le mot n'est pas trouvé,
b pointe vers le mot juste avant et a vers le mot juste après.
"""
a = 0
b = len(dictionnaire) - 1
while a <= b:
m = (a + b) // 2
if dictionnaire[m] == mot:
return (True, None, None)
elif dictionnaire[m] < mot:
a = m + 1
else:
b = m - 1
# TODO : récupérer les voisins avec les indices a et b
# Attention aux cas limites : b peut valoir -1, a peut dépasser la taille
voisin_avant = None # TODO : dictionnaire[b] si b >= 0, sinon None
voisin_apres = None # TODO : dictionnaire[a] si a < len(dictionnaire), sinon None
return (False, voisin_avant, voisin_apres)
def ajouter_mot(dictionnaire, mot):
"""
Insère le mot à la bonne place pour maintenir l'ordre alphabétique.
Principe : tri par insertion sur un seul élément.
"""
dictionnaire.append(mot)
j = len(dictionnaire) - 1
# TODO : décaler vers la gauche tant que le mot précédent est après le mot inséré
while j > 0 and False: # TODO : remplacer False par la bonne condition
dictionnaire[j] = dictionnaire[j - 1]
j -= 1
dictionnaire[j] = mot
def corriger(dictionnaire, mot):
"""Affiche le résultat de la correction pour un mot donné."""
trouve, avant, apres = verifier_avec_voisins(dictionnaire, mot)
if trouve:
print(f' "{mot}" : correct')
else:
suggestions = [v for v in [avant, apres] if v is not None]
if suggestions:
proposition = '" ou "'.join(suggestions)
print(f' "{mot}" : mot inconnu. Vouliez-vous dire "{proposition}" ?')
else:
print(f' "{mot}" : mot inconnu, aucune suggestion.')
# --- Programme principal ---
print("=== Correcteur Orthographique ===")
print(f"Dictionnaire : {len(dictionnaire)} mots\n")
# Tests de correction
print("Tests automatiques :")
for mot in ["python", "pithon", "algorithme", "algorythme", "variable", "varible", "zzz"]:
corriger(dictionnaire, mot)
# Ajout d'un mot
print()
print("Ajout du mot 'reseau' (déjà présent) et 'robot' (nouveau) :")
ajouter_mot(dictionnaire, "robot")
print(f' "robot" ajouté. Vérification : {verifier(dictionnaire, "robot")}')
# Interface interactive
print()
print("--- Mode interactif (tapez 'fin' pour quitter) ---")
while True:
mot = input("Mot à vérifier : ").strip().lower()
if mot == "fin":
break
corriger(dictionnaire, mot)

View File

@@ -0,0 +1,50 @@
# 05 — Le Correcteur Orthographique
**Algorithmes :** Tri par insertion + Recherche dichotomique
**Difficulté :** Moyen
**Durée estimée :** 1h — 1h15
---
## Contexte
Vous construisez un mini correcteur orthographique. Il dispose d'un dictionnaire de mots déjà triés par ordre alphabétique. Il peut vérifier si un mot existe, proposer des mots voisins en cas d'erreur, et permettre d'ajouter de nouveaux mots en maintenant l'ordre.
Ce projet utilise la dichotomie différemment des autres : au lieu de renvoyer seulement `True` ou `False`, elle doit aussi indiquer **où** le mot aurait dû se trouver, pour proposer des suggestions.
---
## Étapes
### Étape 1 — Vérifier un mot
Implémentez `verifier(dictionnaire, mot)` qui retourne `True` si le mot est dans le dictionnaire, `False` sinon.
C'est une recherche dichotomique classique sur des chaînes de caractères.
> Rappel : en Python, les chaînes se comparent alphabétiquement : `"chat" < "chien"` → `True`.
### Étape 2 — Proposer des voisins
Modifiez votre fonction pour qu'elle retourne aussi les **mots voisins** quand le mot est absent : le mot qui se trouverait juste avant et juste après dans le dictionnaire.
> **Indication** : à la fin de la dichotomie, si le mot n'est pas trouvé, les bornes `a` et `b` indiquent l'endroit où il aurait dû être inséré. Les mots en `dictionnaire[b]` et `dictionnaire[a]` sont ses voisins alphabétiques.
Retournez un tuple `(trouve, voisin_avant, voisin_apres)`.
### Étape 3 — Ajouter un mot
Implémentez `ajouter_mot(dictionnaire, mot)` qui insère le mot à la bonne place pour maintenir l'ordre alphabétique.
> **Indication** : c'est le même principe que le tri par insertion, appliqué à un seul élément. Ajoutez le mot à la fin avec `append()`, puis décalez vers la gauche tant que le mot précédent est alphabétiquement après lui.
### Étape 4 — Interface de correction
Complétez la boucle principale : l'utilisateur saisit un mot, le programme répond :
- `"chat" : correct`
- `"chzt" : mot inconnu. Vouliez-vous dire "chute" ou "chat" ?`
### Étape 5 — Extension (si vous avez le temps)
Ajoutez une fonction `charger_mots(fichier)` qui lit un fichier texte (un mot par ligne) et retourne la liste triée. Testez avec un vrai fichier de mots français.
---
## Ce que vous devez rendre
- Le programme fonctionnel avec la suggestion de voisins
- Réponse : pourquoi les bornes `a` et `b` à la fin de la dichotomie indiquent-elles le point d'insertion ?

Binary file not shown.

View File

@@ -0,0 +1,93 @@
# Projet 06 — La Bibliothèque
# Algorithmes : Tri par sélection + Recherche dichotomique
livres = [
{"titre": "le seigneur des anneaux", "auteur": "tolkien", "annee": 1954, "genre": "Fantasy", "pages": 1200},
{"titre": "1984", "auteur": "orwell", "annee": 1949, "genre": "Dystopie", "pages": 328},
{"titre": "dune", "auteur": "herbert", "annee": 1965, "genre": "SF", "pages": 688},
{"titre": "le petit prince", "auteur": "exupery", "annee": 1943, "genre": "Conte", "pages": 96},
{"titre": "fondation", "auteur": "asimov", "annee": 1951, "genre": "SF", "pages": 255},
{"titre": "harry potter t1", "auteur": "rowling", "annee": 1997, "genre": "Fantasy", "pages": 309},
{"titre": "fahrenheit 451", "auteur": "bradbury", "annee": 1953, "genre": "Dystopie", "pages": 158},
{"titre": "l'etranger", "auteur": "camus", "annee": 1942, "genre": "Littérature","pages": 123},
{"titre": "neuromancien", "auteur": "gibson", "annee": 1984, "genre": "SF", "pages": 271},
{"titre": "brave new world", "auteur": "huxley", "annee": 1932, "genre": "Dystopie", "pages": 311},
]
def afficher_catalogue(livres):
"""Affiche le catalogue sous forme de liste numérotée."""
print(f"\n {'Titre':<30} {'Auteur':<12} {'Année':>5} {'Pages':>6}")
print(" " + "-" * 58)
for i, l in enumerate(livres, 1):
print(f" {i:>2}. {l['titre']:<30} {l['auteur']:<12} {l['annee']:>5} {l['pages']:>6} p.")
print()
def afficher_fiche(livre):
"""Affiche la fiche complète d'un livre."""
# TODO : afficher toutes les informations du livre de façon lisible
pass
def tri_selection_livres(livres):
"""Trie les livres par titre alphabétique (tri par sélection)."""
n = len(livres)
for i in range(n):
mini = i
for j in range(i + 1, n):
# TODO : comparer les titres
if ...:
mini = j
livres[i], livres[mini] = livres[mini], livres[i]
return livres
def rechercher_titre(livres_tries, titre):
"""
Recherche dichotomique par titre exact (en minuscules).
Retourne le livre si trouvé, None sinon.
"""
a = 0
b = len(livres_tries) - 1
while a <= b:
m = (a + b) // 2
titre_m = livres_tries[m]["titre"]
if titre_m == titre:
return livres_tries[m]
elif titre_m < titre:
# TODO
pass
else:
# TODO
pass
return None
def statistiques(livres):
"""Affiche des statistiques sur le catalogue."""
n = len(livres)
total_pages = sum(l["pages"] for l in livres)
# TODO : trouver le livre avec le moins de pages et le plus de pages
# Indice : utilise ton tri par sélection sur une copie triée par pages !
print(f"\nStatistiques du catalogue ({n} livres) :")
print(f" Moyenne de pages : {total_pages // n} pages")
# TODO : afficher le plus court et le plus long
# --- Programme principal ---
print("=== Catalogue de la Bibliothèque ===")
print("\nAvant tri :")
afficher_catalogue(livres)
tri_selection_livres(livres)
print("Après tri alphabétique :")
afficher_catalogue(livres)
# Tests de recherche
print("Recherches :")
for titre in ["1984", "dune", "le comte de monte cristo"]:
resultat = rechercher_titre(livres, titre)
if resultat:
print(f" '{titre}' → trouvé !")
afficher_fiche(resultat)
else:
print(f" '{titre}' → absent du catalogue")
statistiques(livres)

View File

@@ -0,0 +1,45 @@
# 06 — La Bibliothèque
**Algorithmes :** Tri par sélection + Recherche dichotomique
**Difficulté :** Moyen
**Durée estimée :** 1h — 1h15
---
## Contexte
Vous gérez le catalogue d'une bibliothèque. Les livres doivent être triés par **titre alphabétique** pour qu'on puisse les retrouver rapidement. Vous utiliserez le tri par sélection pour trier, et la dichotomie pour chercher.
---
## Étapes
### Étape 1 — Trier par titre
Implémentez `tri_selection_livres(livres)` qui trie par `"titre"` (ordre alphabétique).
> **Rappel** : en Python, `"Alice" < "Zorro"` → `True`. Vous pouvez comparer des chaînes directement.
### Étape 2 — Recherche dichotomique
Implémentez `rechercher_titre(livres_tries, titre)` qui retourne le livre si trouvé, `None` sinon.
> **Attention** : la comparaison doit être exacte (majuscules comprises). Pour simplifier, tout est déjà en minuscules dans les données.
### Étape 3 — Affichage enrichi
Implémentez `afficher_fiche(livre)` qui affiche toutes les informations d'un livre de façon lisible (titre, auteur, année, genre, pages).
### Étape 4 — Statistiques
Ajoutez une fonction `statistiques(livres)` qui affiche :
- Le nombre de livres
- Le livre le plus court et le plus long (en pages)
- La moyenne de pages
> **Indication** : une fois la liste triée par titre, vous pouvez utiliser une boucle simple pour les stats. Ou bien trier une copie par pages avec votre tri !
### Étape 5 — Extension (si vous avez le temps)
Ajoutez la possibilité de trier aussi par **année** ou par **nombre de pages**, et de rechercher tous les livres d'un **même auteur** (recherche linéaire cette fois — pourquoi ne peut-on pas utiliser la dichotomie ici ?).
---
## Ce que vous devez rendre
- Le programme fonctionnel
- Réponse : pour chercher tous les livres d'un auteur, pourquoi la dichotomie est-elle inadaptée ?

View File

@@ -0,0 +1,109 @@
# Projet 07 — La Météo
# Algorithmes : Tri par insertion + Recherche dichotomique
# Relevés du mois de mars : (numéro du jour, température en °C)
releves = [
(1, 12.3), (2, 8.7), (3, 14.1), (4, 11.5), (5, 9.2),
(6, 7.8), (7, 13.4), (8, 15.6), (9, 10.1), (10, 6.5),
(11, 5.2), (12, 9.8), (13, 12.7),(14, 16.3),(15, 18.1),
(16, 14.5),(17, 11.2),(18, 8.9), (19, 7.3), (20, 10.5),
(21, 13.8),(22, 17.2),(23, 19.4),(24, 15.1),(25, 12.0),
(26, 9.5), (27, 11.8),(28, 14.2),(29, 16.7),(30, 18.9),
(31, 13.3),
]
def afficher_releves(releves, nb=10):
"""Affiche les nb premiers relevés."""
print(f"\n {'Jour':>5} {'Température':>12}")
print(" " + "-" * 20)
for jour, temp in releves[:nb]:
print(f" Jour {jour:>2} {temp:>8.1f} °C")
if len(releves) > nb:
print(f" ... ({len(releves) - nb} relevés supplémentaires)")
print()
def tri_insertion_temperatures(tab):
"""
Trie les relevés par température croissante (tri par insertion).
Chaque élément est un tuple (jour, temperature).
On trie selon le deuxième élément du tuple.
"""
for i in range(1, len(tab)):
x = tab[i]
j = i
# TODO : comparer tab[j-1][1] et x[1]
while j > 0 and ...:
tab[j] = tab[j - 1]
j -= 1
tab[j] = x
return tab
def statistiques(tab_trie):
"""Affiche les statistiques à partir des relevés triés."""
n = len(tab_trie)
min_releve = tab_trie[0]
max_releve = tab_trie[n - 1]
mediane = tab_trie[n // 2]
print("Statistiques du mois :")
print(f" Minimum : {min_releve[1]:.1f} °C (jour {min_releve[0]})")
print(f" Maximum : {max_releve[1]:.1f} °C (jour {max_releve[0]})")
print(f" Médiane : {mediane[1]:.1f} °C (jour {mediane[0]})")
# TODO : ajouter la moyenne
print()
def temperature_atteinte(tab_trie, t):
"""
Recherche dichotomique : retourne True si la température t
a été relevée au cours du mois (comparaison exacte).
"""
a = 0
b = len(tab_trie) - 1
while a <= b:
m = (a + b) // 2
temp_m = tab_trie[m][1]
if temp_m == t:
return True
elif temp_m < t:
# TODO
pass
else:
# TODO
pass
return False
def histogramme(tab_trie):
"""Affiche un histogramme horizontal des températures (arrondi à l'entier)."""
# Compter les occurrences par degré entier
compteur = {}
for _, temp in tab_trie:
degre = int(temp)
compteur[degre] = compteur.get(degre, 0) + 1
print("Histogramme des températures :")
for degre in sorted(compteur):
barre = "" * compteur[degre]
print(f" {degre:>3}°C | {barre} ({compteur[degre]})")
print()
# --- Programme principal ---
print("=== Analyse Météo — Mars ===")
print("\nRelevés originaux (10 premiers) :")
afficher_releves(releves, nb=5)
# Trier une copie pour ne pas perdre l'ordre chronologique
releves_tries = releves[:]
tri_insertion_temperatures(releves_tries)
print("Relevés triés par température :")
afficher_releves(releves_tries, nb=5)
statistiques(releves_tries)
histogramme(releves_tries)
# Tests de recherche
print("Recherches :")
for t in [5.2, 15.0, 19.4, 20.0]:
present = temperature_atteinte(releves_tries, t)
print(f" {t}°C relevé ce mois-ci ? {'Oui' if present else 'Non'}")

View File

@@ -0,0 +1,54 @@
# 07 — La Météo
**Algorithmes :** Tri par insertion + Recherche dichotomique
**Difficulté :** Moyen
**Durée estimée :** 1h — 1h15
---
## Contexte
Vous analysez les relevés de température d'un mois complet. Vous voulez les trier, calculer des statistiques, et savoir rapidement si une température précise a été atteinte ce mois-ci.
---
## Étapes
### Étape 1 — Trier les températures
Implémentez `tri_insertion_temperatures(tab)` qui trie la liste de relevés par **température croissante**.
Chaque relevé est un tuple `(jour, temperature)`. On trie selon la température (le deuxième élément).
> **Indication** : comparez `tab[j-1][1]` et `x[1]` (le deuxième élément du tuple).
### Étape 2 — Statistiques de base
Implémentez `statistiques(tab_trie)` qui affiche :
- La température **minimale** et le jour où elle a été relevée
- La température **maximale** et le jour où elle a été relevée
- La **médiane** (la valeur au milieu une fois triée)
> Rappel : la médiane d'une liste triée de taille n est à l'indice `n // 2`.
### Étape 3 — Recherche dichotomique
Implémentez `temperature_atteinte(tab_trie, t)` : retourne `True` si la température `t` a été relevée, `False` sinon.
> **Attention** : les relevés sont triés par température. La dichotomie doit comparer `tab[m][1]` (la valeur) avec `t`.
### Étape 4 — Visualisation ASCII
Implémentez `histogramme(tab_trie)` qui affiche un histogramme horizontal des températures.
```
5°C | ##
10°C | #####
15°C | ########
...
```
### Étape 5 — Extension (si vous avez le temps)
Ajoutez une fonction qui regroupe les relevés par **tranche de 5°C** et compte combien de jours tombent dans chaque tranche.
---
## Ce que vous devez rendre
- Le programme complet avec statistiques et recherche
- Réponse : pourquoi le tri par insertion est-il particulièrement efficace si les données sont **déjà presque triées** ?

Binary file not shown.

View File

@@ -0,0 +1,109 @@
# Projet 07 — La Météo
# Algorithmes : Tri par insertion + Recherche dichotomique
# Relevés du mois de mars : (numéro du jour, température en °C)
releves = [
(1, 12.3), (2, 8.7), (3, 14.1), (4, 11.5), (5, 9.2),
(6, 7.8), (7, 13.4), (8, 15.6), (9, 10.1), (10, 6.5),
(11, 5.2), (12, 9.8), (13, 12.7),(14, 16.3),(15, 18.1),
(16, 14.5),(17, 11.2),(18, 8.9), (19, 7.3), (20, 10.5),
(21, 13.8),(22, 17.2),(23, 19.4),(24, 15.1),(25, 12.0),
(26, 9.5), (27, 11.8),(28, 14.2),(29, 16.7),(30, 18.9),
(31, 13.3),
]
def afficher_releves(releves, nb=10):
"""Affiche les nb premiers relevés."""
print(f"\n {'Jour':>5} {'Température':>12}")
print(" " + "-" * 20)
for jour, temp in releves[:nb]:
print(f" Jour {jour:>2} {temp:>8.1f} °C")
if len(releves) > nb:
print(f" ... ({len(releves) - nb} relevés supplémentaires)")
print()
def tri_insertion_temperatures(tab):
"""
Trie les relevés par température croissante (tri par insertion).
Chaque élément est un tuple (jour, temperature).
On trie selon le deuxième élément du tuple.
"""
for i in range(1, len(tab)):
x = tab[i]
j = i
# TODO : comparer tab[j-1][1] et x[1]
while j > 0 and ...:
tab[j] = tab[j - 1]
j -= 1
tab[j] = x
return tab
def statistiques(tab_trie):
"""Affiche les statistiques à partir des relevés triés."""
n = len(tab_trie)
min_releve = tab_trie[0]
max_releve = tab_trie[n - 1]
mediane = tab_trie[n // 2]
print("Statistiques du mois :")
print(f" Minimum : {min_releve[1]:.1f} °C (jour {min_releve[0]})")
print(f" Maximum : {max_releve[1]:.1f} °C (jour {max_releve[0]})")
print(f" Médiane : {mediane[1]:.1f} °C (jour {mediane[0]})")
# TODO : ajouter la moyenne
print()
def temperature_atteinte(tab_trie, t):
"""
Recherche dichotomique : retourne True si la température t
a été relevée au cours du mois (comparaison exacte).
"""
a = 0
b = len(tab_trie) - 1
while a <= b:
m = (a + b) // 2
temp_m = tab_trie[m][1]
if temp_m == t:
return True
elif temp_m < t:
# TODO
pass
else:
# TODO
pass
return False
def histogramme(tab_trie):
"""Affiche un histogramme horizontal des températures (arrondi à l'entier)."""
# Compter les occurrences par degré entier
compteur = {}
for _, temp in tab_trie:
degre = int(temp)
compteur[degre] = compteur.get(degre, 0) + 1
print("Histogramme des températures :")
for degre in sorted(compteur):
barre = "" * compteur[degre]
print(f" {degre:>3}°C | {barre} ({compteur[degre]})")
print()
# --- Programme principal ---
print("=== Analyse Météo — Mars ===")
print("\nRelevés originaux (10 premiers) :")
afficher_releves(releves, nb=5)
# Trier une copie pour ne pas perdre l'ordre chronologique
releves_tries = releves[:]
tri_insertion_temperatures(releves_tries)
print("Relevés triés par température :")
afficher_releves(releves_tries, nb=5)
statistiques(releves_tries)
histogramme(releves_tries)
# Tests de recherche
print("Recherches :")
for t in [5.2, 15.0, 19.4, 20.0]:
present = temperature_atteinte(releves_tries, t)
print(f" {t}°C relevé ce mois-ci ? {'Oui' if present else 'Non'}")

View File

@@ -0,0 +1,54 @@
# 07 — La Météo
**Algorithmes :** Tri par insertion + Recherche dichotomique
**Difficulté :** Moyen
**Durée estimée :** 1h — 1h15
---
## Contexte
Vous analysez les relevés de température d'un mois complet. Vous voulez les trier, calculer des statistiques, et savoir rapidement si une température précise a été atteinte ce mois-ci.
---
## Étapes
### Étape 1 — Trier les températures
Implémentez `tri_insertion_temperatures(tab)` qui trie la liste de relevés par **température croissante**.
Chaque relevé est un tuple `(jour, temperature)`. On trie selon la température (le deuxième élément).
> **Indication** : comparez `tab[j-1][1]` et `x[1]` (le deuxième élément du tuple).
### Étape 2 — Statistiques de base
Implémentez `statistiques(tab_trie)` qui affiche :
- La température **minimale** et le jour où elle a été relevée
- La température **maximale** et le jour où elle a été relevée
- La **médiane** (la valeur au milieu une fois triée)
> Rappel : la médiane d'une liste triée de taille n est à l'indice `n // 2`.
### Étape 3 — Recherche dichotomique
Implémentez `temperature_atteinte(tab_trie, t)` : retourne `True` si la température `t` a été relevée, `False` sinon.
> **Attention** : les relevés sont triés par température. La dichotomie doit comparer `tab[m][1]` (la valeur) avec `t`.
### Étape 4 — Visualisation ASCII
Implémentez `histogramme(tab_trie)` qui affiche un histogramme horizontal des températures.
```
5°C | ##
10°C | #####
15°C | ########
...
```
### Étape 5 — Extension (si vous avez le temps)
Ajoutez une fonction qui regroupe les relevés par **tranche de 5°C** et compte combien de jours tombent dans chaque tranche.
---
## Ce que vous devez rendre
- Le programme complet avec statistiques et recherche
- Réponse : pourquoi le tri par insertion est-il particulièrement efficace si les données sont **déjà presque triées** ?

Binary file not shown.

View File

@@ -0,0 +1,92 @@
# Projet 08 — La Course des Algorithmes
# Comparer les performances du tri par insertion et du tri par sélection
import random
import time
# -------------------------------------------------------
# Étape 1 : recopie ici tes deux fonctions de tri
# -------------------------------------------------------
def tri_insertion(tab):
# TODO : recopier depuis le cours
pass
def tri_selection(tab):
# TODO : recopier depuis le cours
pass
# -------------------------------------------------------
# Outils de mesure (déjà fournis)
# -------------------------------------------------------
def mesurer_temps(fonction, tab):
"""
Mesure le temps d'exécution de fonction(tab) en millisecondes.
Travaille sur une copie pour ne pas modifier l'original.
"""
copie = tab[:]
debut = time.time()
fonction(copie)
fin = time.time()
return round((fin - debut) * 1000, 3) # résultat en ms
def generer_liste(n, mode="aleatoire"):
"""
Génère une liste de n entiers selon le mode :
- "aleatoire" : ordre aléatoire
- "trie" : déjà triée (croissant)
- "inverse" : triée à l'envers (décroissant)
"""
if mode == "aleatoire":
return random.sample(range(n * 10), n)
elif mode == "trie":
return list(range(n))
elif mode == "inverse":
return list(range(n, 0, -1))
# -------------------------------------------------------
# Étape 3 : tableau comparatif selon la taille
# -------------------------------------------------------
def comparer_tailles():
tailles = [100, 500, 1000, 2000, 5000]
print("=== Comparaison par taille (liste aléatoire) ===\n")
print(f" {'n':>6} {'Insertion (ms)':>16} {'Sélection (ms)':>16} {'Vainqueur':>10}")
print(" " + "-" * 55)
for n in tailles:
liste = generer_liste(n, "aleatoire")
t_insertion = mesurer_temps(tri_insertion, liste)
t_selection = mesurer_temps(tri_selection, liste)
vainqueur = "Insertion" if t_insertion < t_selection else "Sélection"
print(f" {n:>6} {t_insertion:>16.3f} {t_selection:>16.3f} {vainqueur:>10}")
print()
# -------------------------------------------------------
# Étape 4 : tableau comparatif selon le mode (n fixé)
# -------------------------------------------------------
def comparer_modes(n=1000):
modes = ["aleatoire", "trie", "inverse"]
print(f"=== Comparaison par configuration (n = {n}) ===\n")
print(f" {'Mode':<12} {'Insertion (ms)':>16} {'Sélection (ms)':>16} {'Vainqueur':>10}")
print(" " + "-" * 60)
for mode in modes:
liste = generer_liste(n, mode)
t_insertion = mesurer_temps(tri_insertion, liste)
t_selection = mesurer_temps(tri_selection, liste)
vainqueur = "Insertion" if t_insertion < t_selection else "Sélection"
print(f" {mode:<12} {t_insertion:>16.3f} {t_selection:>16.3f} {vainqueur:>10}")
print()
# -------------------------------------------------------
# Programme principal
# -------------------------------------------------------
comparer_tailles()
comparer_modes(n=1000)
print("Conclusion :")
print(" TODO : écris tes observations ici")

View File

@@ -0,0 +1,49 @@
# 08 — La Course des Algorithmes
**Algorithmes :** Tri par insertion vs Tri par sélection (comparaison)
**Difficulté :** Difficile
**Durée estimée :** 1h15 — 1h30
---
## Contexte
On sait que les deux tris ont une complexité **quadratique** (O(n²)) dans le pire des cas. Mais en pratique, se comportent-ils vraiment pareil ? C'est ce que vous allez mesurer.
Vous allez tester les deux algorithmes sur différentes tailles de listes et dans différentes configurations, afficher les résultats et tirer des conclusions.
---
## Étapes
### Étape 1 — Copier les deux tris
Recopiez dans le starter vos fonctions `tri_insertion(tab)` et `tri_selection(tab)` qui fonctionnent sur des listes d'entiers.
### Étape 2 — Mesurer le temps
La fonction `mesurer_temps(fonction, tab)` est déjà fournie. Elle prend une **copie** de la liste pour ne pas la modifier et mesure le temps en millisecondes.
> **Important** : toujours travailler sur une **copie** (`tab[:]`) pour que les deux algos partent du même état.
### Étape 3 — Tester sur différentes tailles
Testez pour `n ∈ [100, 500, 1000, 2000, 5000]` avec une liste **aléatoire**. Affichez un tableau comparatif.
### Étape 4 — Tester les cas particuliers
Pour `n = 1000`, testez :
- Liste aléatoire
- Liste **déjà triée** (croissant)
- Liste **triée à l'envers** (décroissant)
Affichez les résultats. Quel algorithme est avantagé selon le cas ?
### Étape 5 — Tracer un graphique (extension)
Si vous connaissez `matplotlib`, tracez les courbes de temps en fonction de n pour les deux algorithmes.
```python
import matplotlib.pyplot as plt
```
---
## Ce que vous devez rendre
- Le programme avec le tableau comparatif
- Une conclusion écrite : dans quels cas préférez-vous le tri par insertion ? Le tri par sélection ? Justifiez avec vos mesures.

Binary file not shown.

View File

@@ -0,0 +1,163 @@
# Projet 09 — Le Générateur Web
# Algorithmes : Tri par insertion + Dichotomie → page HTML/CSS
import webbrowser
jeux = [
{"titre": "The Legend of Zelda: BotW", "studio": "Nintendo", "annee": 2017, "score": 97},
{"titre": "Red Dead Redemption 2", "studio": "Rockstar", "annee": 2018, "score": 97},
{"titre": "Grand Theft Auto V", "studio": "Rockstar", "annee": 2013, "score": 97},
{"titre": "The Witcher 3", "studio": "CD Projekt", "annee": 2015, "score": 93},
{"titre": "Elden Ring", "studio": "FromSoftware", "annee": 2022, "score": 96},
{"titre": "Minecraft", "studio": "Mojang", "annee": 2011, "score": 93},
{"titre": "Portal 2", "studio": "Valve", "annee": 2011, "score": 95},
{"titre": "Hollow Knight", "studio": "Team Cherry", "annee": 2017, "score": 90},
{"titre": "Hades", "studio": "Supergiant", "annee": 2020, "score": 93},
{"titre": "Celeste", "studio": "Maddy Makes", "annee": 2018, "score": 94},
{"titre": "Disco Elysium", "studio": "ZA/UM", "annee": 2019, "score": 97},
{"titre": "Death Stranding", "studio": "Kojima Prod.", "annee": 2019, "score": 82},
]
def tri_insertion_jeux(jeux):
"""
Trie les jeux par score DÉCROISSANT (tri par insertion).
Le meilleur score doit être en premier.
"""
for i in range(1, len(jeux)):
x = jeux[i]
j = i
# TODO : condition pour trier par ordre décroissant
# Indice : pour l'ordre décroissant, on décale quand le score précédent est INFÉRIEUR
while j > 0 and ...:
jeux[j] = jeux[j - 1]
j -= 1
jeux[j] = x
return jeux
def rechercher_score(jeux_tries, score):
"""
Recherche dichotomique d'un jeu par score exact.
ATTENTION : la liste est triée par score DÉCROISSANT.
Les comparaisons < et > sont inversées par rapport au cours !
"""
a = 0
b = len(jeux_tries) - 1
while a <= b:
m = (a + b) // 2
score_m = jeux_tries[m]["score"]
if score_m == score:
return jeux_tries[m]
elif score_m > score:
# Le score du milieu est trop grand → chercher à droite
# TODO
pass
else:
# Le score du milieu est trop petit → chercher à gauche
# TODO
pass
return None
def generer_html(jeux_tries, fichier="classement.html"):
"""Génère une page HTML affichant le classement."""
COULEURS_PODIUM = ["#FFD700", "#C0C0C0", "#CD7F32"] # or, argent, bronze
lignes_html = ""
for i, jeu in enumerate(jeux_tries, 1):
if i <= 3:
bg = f'style="background-color: {COULEURS_PODIUM[i-1]}; font-weight: bold;"'
else:
bg = 'style="background-color: #f9f9f9;"' if i % 2 == 0 else ""
lignes_html += f"""
<tr {bg}>
<td>{i}</td>
<td>{jeu['titre']}</td>
<td>{jeu['studio']}</td>
<td>{jeu['annee']}</td>
<td>{jeu['score']}/100</td>
</tr>"""
html = f"""<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<title>Top Jeux Vidéo</title>
<style>
body {{
font-family: Arial, sans-serif;
max-width: 800px;
margin: 40px auto;
background: #1a1a2e;
color: #eee;
}}
h1 {{
text-align: center;
color: #FFD700;
font-size: 2em;
}}
table {{
width: 100%;
border-collapse: collapse;
background: white;
color: #333;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 4px 20px rgba(0,0,0,0.5);
}}
th {{
background: #16213e;
color: white;
padding: 12px 16px;
text-align: left;
}}
td {{
padding: 10px 16px;
border-bottom: 1px solid #ddd;
}}
tr:last-child td {{ border-bottom: none; }}
</style>
</head>
<body>
<h1>🎮 Classement des meilleurs jeux</h1>
<table>
<thead>
<tr>
<th>#</th>
<th>Titre</th>
<th>Studio</th>
<th>Année</th>
<th>Score</th>
</tr>
</thead>
<tbody>
{lignes_html}
</tbody>
</table>
<p style="text-align:center; margin-top:20px; color:#aaa;">
Généré par Python avec tri par insertion — NSI Première
</p>
</body>
</html>"""
with open(fichier, "w", encoding="utf-8") as f:
f.write(html)
print(f"Page générée : {fichier}")
# --- Programme principal ---
print("=== Générateur de classement HTML ===")
tri_insertion_jeux(jeux)
print("Classement (5 premiers) :")
for i, jeu in enumerate(jeux[:5], 1):
print(f" {i}. {jeu['titre']}{jeu['score']}/100")
# Recherche
jeu_trouve = rechercher_score(jeux, 95)
if jeu_trouve:
print(f"\nJeu avec score 95 : {jeu_trouve['titre']}")
# Génération HTML
generer_html(jeux)
webbrowser.open("classement.html")

View File

@@ -0,0 +1,51 @@
# 09 — Le Générateur Web
**Algorithmes :** Tri par insertion + Recherche dichotomique → génération HTML/CSS
**Difficulté :** Difficile
**Durée estimée :** 1h15 — 1h30
---
## Contexte
Sans JavaScript, une page web ne peut pas exécuter d'algorithme. Mais Python peut **générer** une page HTML ! Vous allez trier des données en Python, puis produire un fichier `.html` affichant le résultat avec un style CSS.
---
## Étapes
### Étape 1 — Trier les données
Implémentez `tri_insertion_jeux(jeux)` qui trie la liste de jeux vidéo par **score décroissant** (le meilleur en premier).
> **Indication** : pour trier par ordre **décroissant**, inversez la condition : `tab[j-1][1] < x[1]` au lieu de `>`.
### Étape 2 — Recherche dichotomique
Implémentez `rechercher_score(jeux_tries, score)` qui retourne le jeu avec ce score exact, ou `None`.
> Attention : la liste est triée par score **décroissant**. La dichotomie doit être adaptée (les comparaisons `<` et `>` sont inversées).
### Étape 3 — Générer la page HTML
Implémentez `generer_html(jeux_tries, fichier)` qui écrit un fichier `.html` contenant un tableau du classement, stylisé avec du CSS intégré.
La page doit afficher au minimum :
- Le rang, le titre, le studio, l'année et le score de chaque jeu
- Les 3 premiers avec une couleur distincte (or, argent, bronze)
- Un titre et un style propre
### Étape 4 — Ouvrir la page automatiquement
À la fin du programme, ouvrez la page générée dans le navigateur avec :
```python
import webbrowser
webbrowser.open("classement.html")
```
### Étape 5 — Extension (si vous avez le temps)
Ajoutez un deuxième tableau sur la même page : les jeux triés par **année de sortie** (vous pouvez faire un deuxième tri sur une copie).
---
## Ce que vous devez rendre
- Le fichier `starter.py` qui tourne et génère `classement.html`
- La page `classement.html` ouverte dans le navigateur
- Réponse : pourquoi Python peut-il générer du HTML, alors qu'HTML ne peut pas exécuter Python ?

View File

@@ -0,0 +1,238 @@
# Guide enseignant — Questions fréquentes par projet
---
## Questions communes à tous les projets
**"On peut utiliser `sort()` ou `sorted()` ?"**
Non. L'objectif est précisément d'implémenter les algorithmes vus en cours. Ces fonctions intégrées existent, mais leur utilisation ici invalide le travail demandé.
**"Comment copier une liste sans modifier l'original ?"**
```python
copie = ma_liste[:] # ou list(ma_liste)
```
Ne pas faire `copie = ma_liste` : les deux variables pointeraient sur le même objet en mémoire.
**"La dichotomie ne fonctionne pas, j'ai une erreur ou un résultat faux."**
Vérifier que la liste est bien triée avant d'appeler la dichotomie. C'est la condition indispensable. Si la liste n'est pas triée, le résultat est imprévisible.
**"Mon tri modifie la liste originale, est-ce normal ?"**
Oui. `tri_insertion` et `tri_selection` trient la liste **en place** (ils la modifient directement). Si l'on veut conserver l'original, il faut travailler sur une copie : `tri_insertion(ma_liste[:])`.
**"J'ai une `IndexError`."**
La cause la plus fréquente est un indice qui dépasse la taille de la liste. Vérifiez les bornes de vos boucles (`range`) et les conditions d'arrêt (`while`).
---
## Projet 01 — Le Nombre Mystère
**"Pourquoi la condition est `while gauche <= droite` et pas `while gauche < droite` ?"**
Quand il ne reste qu'un seul élément possible, `gauche` et `droite` sont égaux. Si on exclut ce cas avec `<`, on rate le dernier candidat. Le `<=` permet de traiter ce dernier cas.
**"Pourquoi 7 essais suffisent toujours pour 100 nombres ?"**
Parce que la recherche dichotomique a une complexité logarithmique : log₂(100) ≈ 6,64, donc 7 essais au maximum. À chaque question, on élimine la moitié des candidats restants.
**"Et si l'utilisateur ment ou se trompe dans sa réponse ?"**
Le programme finira par épuiser l'intervalle et afficher "tu as triché". Ce comportement est déjà géré dans le starter (fin de boucle sans `return`).
**"Comment modifier `gauche` et `droite` selon la réponse ?"**
- Si le nombre cherché est `<` milieu : `droite = milieu - 1`
- Si le nombre cherché est `>` milieu : `gauche = milieu + 1`
---
## Projet 02 — Le Tri de Cartes
**"Comment accéder à la valeur d'une carte ?"**
Chaque carte est un tuple `(valeur, couleur)`. `carte[0]` donne la valeur, `carte[1]` donne la couleur.
**"Pourquoi l'As vaut 14 ?"**
C'est une convention pour qu'il soit la carte la plus forte (valeur maximale). Cela permet de le trier après le Roi (13) avec une simple comparaison numérique.
**"La condition du `while` dans le tri insertion : que faut-il écrire ?"**
```python
while j > 0 and main[j - 1][0] > x[0]:
```
On compare uniquement les valeurs (`[0]`), pas les couleurs.
**"Ma dichotomie retourne toujours `False` même si la carte est là."**
Vérifier que la main est bien triée avant la recherche. Et que la comparaison porte bien sur `main_triee[m][0]` (la valeur), pas sur la carte entière.
---
## Projet 03 — Le Podium
**"Comment trier des dictionnaires ?"**
On ne trie pas le dictionnaire en tant que tel, on trie la **liste** de dictionnaires. Dans le tri, on compare `athletes[j]["temps"]` et `athletes[mini]["temps"]`.
**"Que se passe-t-il si deux athlètes ont le même temps ?"**
Le tri par sélection n'est pas **stable** : il ne garantit pas que deux éléments égaux restent dans leur ordre d'origine. L'un des deux sera placé en premier de façon arbitraire. C'est une propriété à connaître pour le DS.
**"Comment aligner le texte dans le podium ASCII ?"**
Avec les f-strings et le formatage d'alignement :
```python
f"{nom:^12}" # centré sur 12 caractères
f"{nom:<12}" # aligné à gauche
f"{nom:>12}" # aligné à droite
```
**"La dichotomie sur les temps : pourquoi ça compare des flottants ?"**
Comparer des flottants avec `==` peut poser des problèmes de précision en Python (ex. `0.1 + 0.2 != 0.3`). Ici les données sont saisies à la main donc c'est acceptable, mais c'est un point de vigilance à mentionner.
---
## Projet 04 — L'Annuaire
**"Comment ajouter un élément à la fin d'une liste ?"**
```python
ma_liste.append(element)
```
**"Comment supprimer un élément d'une liste par sa valeur ?"**
```python
ma_liste.remove(element) # supprime la première occurrence
```
Ou par indice :
```python
del ma_liste[i]
```
**"La condition du `while` dans `ajouter_contact` : que comparer ?"**
```python
while j > 0 and annuaire[j - 1]["nom"] > contact["nom"]:
```
On compare les noms alphabétiquement.
**"La dichotomie me retourne le bon contact mais je n'affiche rien."**
Vérifier que l'appel dans le `menu()` est bien `resultat = rechercher(annuaire, nom)` et que le résultat est ensuite utilisé (`if resultat is not None:`).
**"Le menu ne s'arrête jamais."**
C'est le comportement voulu (`while True`). Il faut saisir `4` pour déclencher le `break`.
---
## Projet 05 — Le Correcteur Orthographique
**"Pourquoi les bornes `a` et `b` donnent les voisins à la fin de la dichotomie ?"**
À la fin de la boucle (quand `a > b`), `b` vaut l'indice du dernier élément plus petit que le mot cherché, et `a` vaut l'indice du premier élément plus grand. Ce sont donc les deux voisins alphabétiques du mot absent. C'est une propriété de la dichotomie que l'on exploite ici de façon non conventionnelle.
**"Il faut vérifier quoi pour les cas limites des voisins ?"**
- Si `b < 0` : le mot serait avant tous les mots du dictionnaire → pas de voisin avant
- Si `a >= len(dictionnaire)` : le mot serait après tous les mots → pas de voisin après
```python
voisin_avant = dictionnaire[b] if b >= 0 else None
voisin_apres = dictionnaire[a] if a < len(dictionnaire) else None
```
**"La condition du `while` dans `ajouter_mot` : que comparer ?"**
```python
while j > 0 and dictionnaire[j - 1] > mot:
```
Les chaînes se comparent directement en Python (ordre alphabétique).
**"Les suggestions ne correspondent pas au mot saisi."**
Vérifier que la comparaison se fait bien en minuscules. Utiliser `.lower()` sur le mot saisi par l'utilisateur pour uniformiser.
---
## Projet 06 — La Bibliothèque
**"Comment trier par titre (chaînes) avec le tri sélection ?"**
```python
if livres[j]["titre"] < livres[mini]["titre"]:
```
Python compare les chaînes alphabétiquement avec `<` et `>`.
**"La dichotomie ne trouve pas un livre qui est pourtant dans la liste."**
Les titres dans le dictionnaire sont tous en **minuscules**. Si l'utilisateur saisit "1984" avec une majuscule ou des espaces en trop, la comparaison échouera. Appliquer `.strip().lower()` à la saisie.
**"Comment trier une copie sans modifier la liste originale ?"**
```python
livres_par_pages = livres[:] # copie
tri_selection_livres_par_pages(livres_par_pages) # trier la copie
```
**"Comment calculer la moyenne ?"**
```python
total = sum(l["pages"] for l in livres)
moyenne = total / len(livres)
```
---
## Projet 07 — La Météo
**"Comment accéder au jour et à la température dans un tuple ?"**
Chaque relevé est `(jour, temperature)`. On accède avec `releve[0]` pour le jour et `releve[1]` pour la température. Ou avec le déballage : `jour, temp = releve`.
**"La condition du `while` dans le tri : que comparer ?"**
```python
while j > 0 and tab[j - 1][1] > x[1]:
```
On compare le deuxième élément du tuple (la température).
**"Qu'est-ce que `.get()` sur un dictionnaire ?"**
`compteur.get(degre, 0)` retourne la valeur associée à la clé `degre` si elle existe, sinon retourne `0`. C'est équivalent à :
```python
if degre in compteur:
return compteur[degre]
else:
return 0
```
**"Pourquoi travailler sur une copie des relevés ?"**
Pour conserver l'ordre chronologique original. Si on trie directement `releves`, on perd l'information "quel jour avait quelle température".
**"Comment calculer la médiane ?"**
Sur une liste triée de taille `n`, la médiane est l'élément à l'indice `n // 2` :
```python
mediane = tab_trie[len(tab_trie) // 2]
```
---
## Projet 08 — La Course des Algorithmes
**"Les temps mesurés varient à chaque exécution, c'est normal ?"**
Oui. Le temps dépend de la charge du processeur au moment de l'exécution. Pour des résultats plus stables, on peut faire la moyenne sur plusieurs exécutions. Mais les tendances (quel algorithme est plus rapide) restent les mêmes.
**"Pourquoi travailler sur une copie dans `mesurer_temps` ?"**
Si on trie directement la liste passée en argument, elle sera triée pour la deuxième mesure : les deux algorithmes ne partiraient pas dans les mêmes conditions.
**"Mon tri insertion est beaucoup plus rapide sur une liste déjà triée, pourquoi ?"**
Parce que sur une liste déjà triée, la boucle `while` interne ne s'exécute jamais (la condition est fausse dès le premier tour). Sa complexité devient linéaire O(n) au lieu de O(n²). Ce n'est pas le cas du tri sélection, qui parcourt toujours toute la liste restante.
**"Qu'est-ce que `random.sample(range(n * 10), n)` ?"**
Cela génère une liste de `n` entiers distincts choisis aléatoirement entre 0 et `n*10`. Le `* 10` évite les doublons dans la liste.
**"Comment interpréter les résultats pour la conclusion ?"**
Le tri insertion est plus rapide sur des listes presque triées. Le tri sélection est plus constant (toujours O(n²) quel que soit le cas). Pour des listes aléatoires de grande taille, les deux sont comparables.
---
## Projet 09 — Le Générateur Web
**"Le fichier HTML se génère où ?"**
Dans le même dossier que `starter.py`. Si le navigateur ne s'ouvre pas automatiquement, ouvrir manuellement `classement.html` depuis ce dossier.
**"La dichotomie inversée : comment ça marche ?"**
Quand la liste est triée par ordre décroissant, un score plus grand se trouve à gauche. Les comparaisons sont donc inversées :
- Si `score_m > score` (le milieu est trop grand) → chercher à droite (`a = m + 1`)
- Si `score_m < score` (le milieu est trop petit) → chercher à gauche (`b = m - 1`)
**"La condition du `while` dans le tri décroissant : que mettre ?"**
```python
while j > 0 and jeux[j - 1]["score"] < x["score"]:
```
On décale quand le score précédent est **inférieur** (l'inverse du tri croissant).
**"Comment écrire dans un fichier en Python ?"**
```python
with open("fichier.html", "w", encoding="utf-8") as f:
f.write(contenu)
```
Le `with` garantit que le fichier est bien fermé après l'écriture.
**"Pourquoi Python peut générer du HTML mais HTML ne peut pas exécuter Python ?"**
HTML est un langage de **description** (il décrit une structure), pas un langage d'exécution. Python, lui, s'exécute côté serveur ou en local et peut produire n'importe quel texte en sortie, y compris du HTML. Un navigateur lit le HTML généré, mais ne sait pas d'où il vient.

View File

@@ -0,0 +1,65 @@
# Mini-projets — Algorithmes de tri et recherche dichotomique
Séance de révision avant DS (mardi). Chaque groupe reçoit un projet différent.
Durée : **1h à 1h30**. Langage : **Python** (sauf projet 09 : Python + HTML/CSS).
> **Règle commune** : vous devez implémenter vous-mêmes `tri_insertion`, `tri_selection` et `dichotomique`. **Interdiction d'utiliser `sort()` ou `sorted()`.**
---
## Répartition des groupes
| # | Projet | Algorithmes utilisés | Difficulté |
|---|--------|----------------------|------------|
| 01 | [Le Nombre Mystère](01_devine_nombre/) | Dichotomie | Facile |
| 02 | [Le Tri de Cartes](02_cartes/) | Tri insertion | Facile |
| 03 | [Le Podium](03_podium/) | Tri sélection + Dichotomie | Facile |
| 04 | [L'Annuaire](04_annuaire/) | Tri insertion + Dichotomie | Moyen |
| 05 | [Le Correcteur](05_correcteur/) | Tri insertion + Dichotomie (voisins) | Moyen |
| 06 | [La Bibliothèque](06_bibliotheque/) | Tri sélection + Dichotomie | Moyen |
| 07 | [La Météo](07_meteo/) | Tri insertion + Dichotomie | Moyen |
| 08 | [La Course des Algos](08_course_algos/) | Tri insertion vs Tri sélection | Difficile |
| 09 | [Le Générateur Web](09_generateur_web/) | Tri insertion + Dichotomie → HTML | Difficile |
---
## Rappels des algorithmes
```python
def tri_insertion(tab):
for i in range(1, len(tab)):
x = tab[i]
j = i
while j > 0 and tab[j - 1] > x:
tab[j] = tab[j - 1]
j -= 1
tab[j] = x
return tab
def tri_selection(tab):
n = len(tab)
for i in range(n):
mini = i
for j in range(i + 1, n):
if tab[j] < tab[mini]:
mini = j
tab[i], tab[mini] = tab[mini], tab[i]
return tab
def dichotomique(tab, x):
a = 0
b = len(tab) - 1
while a <= b:
m = (a + b) // 2
if tab[m] == x:
return True
elif tab[m] < x:
a = m + 1
else:
b = m - 1
return False
```
---
Auteur : Florian Mathieu — Licence CC BY NC