Ch. 00 · Leçon 2
Mini-projet 1 — Gestion d'une bibliothèque scolaire
Ce que vous saurez faire
- Analyser un cahier des charges réel en sous-problèmes
- Combiner tableaux, recherche et tri dans un programme complet
- Implémenter un menu interactif avec validation de saisie
- Justifier les choix de structures à chaque étape
Une bibliothèque, 50 livres, 4 chapitres mobilisés
Ce mini-projet est un sujet bac type qui mobilise les notions des 4 chapitres officiels. Donnez-vous 2 heures, traitez-le dans les conditions de l'examen, puis comparez votre solution au corrigé.
Énoncé
Une bibliothèque scolaire gère ses livres avec un programme. Chaque livre est caractérisé par :
- son titre (chaîne de caractères),
- son nombre d'emprunts depuis le début de l'année (entier).
La bibliothèque contient au maximum 50 livres. Le programme propose un menu :
=== Gestion bibliothèque ===
1. Ajouter un livre
2. Enregistrer un emprunt
3. Afficher les N livres les plus empruntés
4. Rechercher un livre par titre (recherche exacte)
0. Quitter
Travail demandé
- Analyse modulaire : proposer un arbre d'analyse avec les modules nécessaires.
- T.D.O complet du programme principal.
- Algorithme détaillé de chaque sous-programme.
- Traduction Python 3 de l'ensemble.
- Justifications : structure de stockage, choix des boucles, choix de l'algorithme de tri.
Pistes (à ne consulter qu'après une vraie tentative)
💡 Indice 1 — Comment stocker 50 livres ?
Comme on n'a pas étudié les structures composées (records / classes), on utilise deux tableaux parallèles :
titres[50] : tableau de chaînes
emprunts[50] : tableau d'entiers
nb_livres : entier (nombre actuel de livres, ≤ 50)
Le livre d'indice i a comme titre titres[i] et comme emprunts emprunts[i].
💡 Indice 2 — Quel algorithme pour le top N le plus emprunté ?
Triez les deux tableaux en parallèle (par emprunts décroissants), puis affichez les N premiers titres. Le tri par sélection est le plus simple : à chaque passe, trouvez l'indice du max restant, échangez T[i] avec T[indice_max] dans les deux tableaux simultanément.
💡 Indice 3 — Quelle recherche pour un livre par titre ?
Les titres ne sont pas triés par défaut (ils sont triés par emprunts après le tri du point précédent). Donc : recherche séquentielle (O(N)). La dichotomie nécessiterait de re-trier par titre.
Corrigé complet
📘 Voir le corrigé (à n'ouvrir qu'après votre tentative)
1. Analyse modulaire
ProgrammePrincipal
│
┌────────┬─────────┬───┴──────┬──────────┬──────────┐
▼ ▼ ▼ ▼ ▼ ▼
afficher lire_ ajouter_ enregistrer_ top_n_ rechercher_
_menu choix livre emprunt empruntes par_titre
│ │
▼ ▼
chercher_idx trier_par_emprunts
_du_titre _decroissants
2. T.D.O programme principal
T.D.O
Objet | Type / Nature | Rôle
------------|-------------------------------|----------------------------------
titres | Tableau de 50 chaînes | Titres des livres
emprunts | Tableau de 50 entiers | Nombre d'emprunts par livre
nb_livres | Variable / entier | Nombre actuel de livres (0..50)
choix | Variable / entier | Choix de l'utilisateur dans le menu
MAX_LIVRES | Constante / entier = 50 | Capacité maximale
3. Code Python complet
MAX_LIVRES = 50
def afficher_menu():
print("=== Gestion bibliothèque ===")
print("1. Ajouter un livre")
print("2. Enregistrer un emprunt")
print("3. Afficher les N livres les plus empruntés")
print("4. Rechercher un livre par titre")
print("0. Quitter")
def lire_choix() -> int:
"""Lit un choix valide (0-4)."""
while True:
try:
c = int(input("Votre choix : "))
if 0 <= c <= 4:
return c
except ValueError:
pass
print("Choix invalide, recommencer.")
def ajouter_livre(titres, emprunts, nb_livres) -> int:
"""Retourne le nouveau nb_livres."""
if nb_livres >= MAX_LIVRES:
print("Bibliothèque pleine.")
return nb_livres
titre = input("Titre du nouveau livre : ").strip()
titres[nb_livres] = titre
emprunts[nb_livres] = 0
return nb_livres + 1
def chercher_idx_du_titre(titres, nb_livres, cible) -> int:
"""Recherche séquentielle : retourne l'indice ou -1."""
cible_lower = cible.lower()
for i in range(nb_livres):
if titres[i].lower() == cible_lower:
return i
return -1
def enregistrer_emprunt(titres, emprunts, nb_livres) -> None:
titre = input("Titre du livre emprunté : ").strip()
idx = chercher_idx_du_titre(titres, nb_livres, titre)
if idx == -1:
print(f"Livre « {titre} » introuvable.")
return
emprunts[idx] += 1
print(f"Emprunt enregistré. Total : {emprunts[idx]}.")
def trier_par_emprunts_decroissants(titres, emprunts, nb_livres) -> None:
"""Tri par sélection des deux tableaux en parallèle."""
for i in range(nb_livres - 1):
idx_max = i
for j in range(i + 1, nb_livres):
if emprunts[j] > emprunts[idx_max]:
idx_max = j
# Échange dans les deux tableaux
titres[i], titres[idx_max] = titres[idx_max], titres[i]
emprunts[i], emprunts[idx_max] = emprunts[idx_max], emprunts[i]
def top_n_empruntes(titres, emprunts, nb_livres) -> None:
n = int(input(f"Combien de livres afficher (1..{nb_livres}) ? "))
n = min(max(1, n), nb_livres)
trier_par_emprunts_decroissants(titres, emprunts, nb_livres)
print(f"\n--- Top {n} ---")
for i in range(n):
print(f"{i + 1}. {titres[i]} ({emprunts[i]} emprunts)")
def rechercher_par_titre(titres, nb_livres) -> None:
cible = input("Titre à rechercher : ").strip()
idx = chercher_idx_du_titre(titres, nb_livres, cible)
if idx == -1:
print("Livre introuvable.")
else:
print(f"Trouvé à l'indice {idx}.")
# === Programme principal ===
titres = [""] * MAX_LIVRES
emprunts = [0] * MAX_LIVRES
nb_livres = 0
while True:
afficher_menu()
choix = lire_choix()
if choix == 0:
break
elif choix == 1:
nb_livres = ajouter_livre(titres, emprunts, nb_livres)
elif choix == 2:
enregistrer_emprunt(titres, emprunts, nb_livres)
elif choix == 3:
top_n_empruntes(titres, emprunts, nb_livres)
elif choix == 4:
rechercher_par_titre(titres, nb_livres)
print("Au revoir.")
4. Justifications
- Tableaux parallèles : choix forcé car pas de type composé au programme.
titres[i]↔emprunts[i]. - Recherche séquentielle : les titres ne sont pas triés alphabétiquement, donc dichotomie impossible.
- Tri par sélection : algorithme simple, O(n²) acceptable pour n = 50 (2500 opérations max → instantané).
- Tri parallèle : on permute simultanément dans les deux tableaux pour préserver l'association.
- Menu — boucle
while True / break: équivaut àRépéter … Jusqu'à choix = 0. On exécute au moins une fois.
5. Cas limites traités
- Bibliothèque vide (nb_livres = 0) → menu marche, recherche retourne -1, top N affiche 0 livres.
- Bibliothèque pleine (nb_livres = 50) → ajout refusé proprement.
- Titre déjà existant → on permet le doublon (peut être un point d'amélioration).
- Comparaison de titres insensible à la casse (
.lower()).
Auto-évaluation
Cochez ce que vous avez réussi :
- Arbre d'analyse complet (au moins 6 modules)
- T.D.O exhaustif
- Algorithme de tri par sélection en parallèle sur 2 tableaux
- Recherche séquentielle correcte
- Boucle
Répéter…Jusqu'à(ouwhile True/break) pour le menu - Justifications écrites pour chaque choix
Barème indicatif : 4 pts par item coché ≥ 20/20.