ajout de tous les cours et TP préparés cet été

This commit is contained in:
2026-01-17 23:10:49 +01:00
parent ed9415bc81
commit 301cf5a98f
125 changed files with 21614 additions and 542 deletions

245
Routage/CORRIGE.md Normal file
View File

@@ -0,0 +1,245 @@
# Corrigé des exercices — Routage
---
## Exercice 1 : Tables de routage
Soit le réseau suivant :
```
A ---1--- B ---2--- E
| |
3 5
| |
C ---4--- D ---1--- F
\ /
2 1
\ /
G
```
### Question 1 : Compléter la table de routage du routeur A
| Destination | Passerelle | Distance (sauts) |
|-------------|------------|------------------|
| B | B | 1 |
| C | C | 1 |
| D | C | 2 |
| E | B | 2 |
| F | C | 3 |
| G | C | 2 |
**Explication** : Depuis A, pour atteindre :
- B : directement connecté (1 saut)
- C : directement connecté (1 saut)
- D : via C (A→C→D = 2 sauts)
- E : via B (A→B→E = 2 sauts)
- F : via C puis D (A→C→D→F = 3 sauts)
- G : via C (A→C→G = 2 sauts)
### Question 2 : Compléter la table de routage du routeur D
| Destination | Passerelle | Distance (sauts) |
|-------------|------------|------------------|
| A | C | 2 |
| B | B | 1 |
| C | C | 1 |
| E | B | 2 |
| F | F | 1 |
| G | G | 1 |
---
## Exercice 2 : Protocole RIP
### Question 1 : Principe de mise à jour
Un routeur R reçoit de son voisin V la table suivante :
| Destination | Distance |
|-------------|----------|
| X | 2 |
| Y | 4 |
| Z | 1 |
Si R est à distance 1 de V, quelle sera la mise à jour de la table de R ?
**Réponse** :
R ajoute 1 (la distance vers V) à chaque entrée :
| Destination | Distance via V |
|-------------|----------------|
| X | 2 + 1 = 3 |
| Y | 4 + 1 = 5 |
| Z | 1 + 1 = 2 |
R ne mettra à jour sa table que si ces distances sont **inférieures** aux distances actuellement connues.
### Question 2 : Limite de RIP
Pourquoi RIP est-il limité à 15 sauts maximum ?
**Réponse** :
- Au-delà de 15 sauts, la destination est considérée comme inaccessible (distance = 16 = infini)
- Cette limite évite les boucles infinies de routage
- Elle limite aussi la taille des réseaux pouvant utiliser RIP
- C'est pourquoi RIP est adapté aux petits réseaux seulement
### Question 3 : Convergence
Combien de temps faut-il au minimum pour qu'une information de routage traverse 5 routeurs avec RIP ?
**Réponse** :
- RIP envoie des mises à jour toutes les 30 secondes
- Pour traverser 5 routeurs : 5 × 30 = **150 secondes** (2 min 30 s) minimum
---
## Exercice 3 : Algorithme de Dijkstra
### Question 1 : Exécution manuelle
Soit le graphe pondéré suivant :
```
4
A -----> B
| |
2| |1
v v
C -----> D -----> E
1 3
```
Appliquer l'algorithme de Dijkstra depuis A.
**Résolution pas à pas** :
| Étape | Sommet traité | d(A) | d(B) | d(C) | d(D) | d(E) |
|-------|---------------|------|------|------|------|------|
| Init | - | 0 | ∞ | ∞ | ∞ | ∞ |
| 1 | A | 0 | 4 | 2 | ∞ | ∞ |
| 2 | C | 0 | 4 | 2 | 3 | ∞ |
| 3 | D | 0 | 4 | 2 | 3 | 6 |
| 4 | B | 0 | 4 | 2 | 3 | 6 |
| 5 | E | 0 | 4 | 2 | 3 | 6 |
**Résultat final** :
- A → A : 0
- A → B : 4 (chemin direct)
- A → C : 2 (chemin direct)
- A → D : 3 (via C)
- A → E : 6 (via C puis D)
### Question 2 : Reconstruction du chemin
Quel est le plus court chemin de A vers E ?
**Réponse** : A → C → D → E (coût total : 6)
---
## Exercice 4 : Protocole OSPF
### Question 1 : Calcul du coût d'une liaison
La formule OSPF est : `coût = 10^8 / débit`
Calculer le coût des liaisons suivantes :
| Type de liaison | Débit | Coût |
|-----------------|-------|------|
| Ethernet 10 Mbps | 10 × 10^6 bps | 10^8 / 10^7 = **10** |
| Fast Ethernet 100 Mbps | 100 × 10^6 bps | 10^8 / 10^8 = **1** |
| Gigabit Ethernet | 10^9 bps | 10^8 / 10^9 = **0.1 ≈ 1** |
| Liaison série 2 Mbps | 2 × 10^6 bps | 10^8 / (2×10^6) = **50** |
**Note** : En pratique, le coût minimum est souvent fixé à 1.
### Question 2 : Choix du meilleur chemin
Soit un réseau avec deux chemins possibles de A vers Z :
- Chemin 1 : A → B → Z avec liaisons à 100 Mbps et 10 Mbps
- Chemin 2 : A → C → D → Z avec trois liaisons à 100 Mbps
Quel chemin OSPF choisira-t-il ?
**Calcul** :
- Chemin 1 : coût = 1 + 10 = **11**
- Chemin 2 : coût = 1 + 1 + 1 = **3**
**Réponse** : OSPF choisira le **chemin 2** car son coût (3) est inférieur à celui du chemin 1 (11), même s'il traverse plus de routeurs.
---
## Exercice 5 : Comparaison RIP vs OSPF
### Question : Différences fondamentales
| Critère | RIP | OSPF |
|---------|-----|------|
| Type d'algorithme | Vecteur de distance (Bellman-Ford) | État de liens (Dijkstra) |
| Métrique | Nombre de sauts | Coût (basé sur le débit) |
| Vision du réseau | Locale (voisins uniquement) | Globale (carte complète) |
| Limite | 15 sauts max | Pas de limite de sauts |
| Convergence | Lente (30s entre mises à jour) | Rapide |
| Complexité | Simple | Plus complexe |
| Adapté pour | Petits réseaux | Grands réseaux |
---
## Exercice 6 : Implémentation Python de Dijkstra
```python
def dijkstra(graphe, source):
"""
Algorithme de Dijkstra pour trouver les plus courts chemins.
:param graphe: dict {sommet: [(voisin, poids), ...]}
:param source: sommet de départ
:return: dict des distances minimales
"""
# Initialisation
distances = {sommet: float('inf') for sommet in graphe}
distances[source] = 0
non_visites = set(graphe.keys())
while non_visites:
# Sélectionner le sommet non visité avec la plus petite distance
courant = min(non_visites, key=lambda s: distances[s])
non_visites.remove(courant)
# Si la distance est infinie, les sommets restants sont inaccessibles
if distances[courant] == float('inf'):
break
# Mettre à jour les distances des voisins
for voisin, poids in graphe[courant]:
nouvelle_distance = distances[courant] + poids
if nouvelle_distance < distances[voisin]:
distances[voisin] = nouvelle_distance
return distances
# Test
graphe = {
'A': [('B', 4), ('C', 2)],
'B': [('D', 1)],
'C': [('D', 1)],
'D': [('E', 3)],
'E': []
}
print(dijkstra(graphe, 'A'))
# Résultat : {'A': 0, 'B': 4, 'C': 2, 'D': 3, 'E': 6}
```
---
Auteur : Florian Mathieu
Licence CC BY NC
<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"><img alt="Licence Creative Commons" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png" /></a> <br />Ce cours est mis à disposition selon les termes de la <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/">Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International</a>.

View File

@@ -0,0 +1,415 @@
"""
Corrigé du TP GPS Navigator — Itinéraires optimaux
"""
# =============================================================================
# PARTIE 1 : Modélisation du réseau
# =============================================================================
class ReseauRoutier:
"""Représente un réseau routier sous forme de graphe."""
def __init__(self):
"""Initialise un réseau vide."""
self.graphe = {}
def ajouter_ville(self, ville):
"""
Ajoute une ville (sommet) au réseau.
:param ville: (str) Nom de la ville
"""
if ville not in self.graphe:
self.graphe[ville] = []
def ajouter_route(self, ville1, ville2, distance):
"""
Ajoute une route bidirectionnelle entre deux villes.
:param ville1: (str) Première ville
:param ville2: (str) Deuxième ville
:param distance: (int) Distance en km
"""
# S'assurer que les villes existent
self.ajouter_ville(ville1)
self.ajouter_ville(ville2)
# Ajouter la route dans les deux sens
self.graphe[ville1].append((ville2, distance))
self.graphe[ville2].append((ville1, distance))
def get_voisins(self, ville):
"""
Retourne la liste des voisins d'une ville avec les distances.
:param ville: (str) Nom de la ville
:return: (list) Liste de tuples (voisin, distance)
"""
return self.graphe.get(ville, [])
def afficher(self):
"""Affiche le réseau."""
for ville, voisins in self.graphe.items():
print(f"{ville} -> {voisins}")
# =============================================================================
# PARTIE 2 : Algorithme de Dijkstra
# =============================================================================
def selectionner_minimum(distances, non_visites):
"""
Sélectionne le sommet non visité avec la distance minimale.
:param distances: (dict) Distances actuelles
:param non_visites: (set) Ensemble des sommets non visités
:return: (str) Le sommet avec la distance minimale
"""
minimum = None
distance_min = float('inf')
for sommet in non_visites:
if distances[sommet] < distance_min:
distance_min = distances[sommet]
minimum = sommet
return minimum
def dijkstra(reseau, depart):
"""
Algorithme de Dijkstra pour trouver les plus courts chemins.
:param reseau: (ReseauRoutier) Le réseau routier
:param depart: (str) Ville de départ
:return: (dict) Distances minimales depuis le départ
"""
# Initialisation
distances = {ville: float('inf') for ville in reseau.graphe}
distances[depart] = 0
non_visites = set(reseau.graphe.keys())
while non_visites:
# Sélectionner le sommet avec la distance minimale
courant = selectionner_minimum(distances, non_visites)
if courant is None or distances[courant] == float('inf'):
break
non_visites.remove(courant)
# Mettre à jour les distances des voisins
for voisin, poids in reseau.get_voisins(courant):
nouvelle_distance = distances[courant] + poids
if nouvelle_distance < distances[voisin]:
distances[voisin] = nouvelle_distance
return distances
# =============================================================================
# PARTIE 3 : Reconstruction du chemin
# =============================================================================
def dijkstra_avec_chemin(reseau, depart):
"""
Dijkstra avec mémorisation des prédécesseurs.
:param reseau: (ReseauRoutier) Le réseau routier
:param depart: (str) Ville de départ
:return: (tuple) (distances, predecesseurs)
"""
distances = {ville: float('inf') for ville in reseau.graphe}
predecesseurs = {ville: None for ville in reseau.graphe}
distances[depart] = 0
non_visites = set(reseau.graphe.keys())
while non_visites:
courant = selectionner_minimum(distances, non_visites)
if courant is None or distances[courant] == float('inf'):
break
non_visites.remove(courant)
for voisin, poids in reseau.get_voisins(courant):
nouvelle_distance = distances[courant] + poids
if nouvelle_distance < distances[voisin]:
distances[voisin] = nouvelle_distance
predecesseurs[voisin] = courant
return distances, predecesseurs
def reconstruire_chemin(predecesseurs, depart, arrivee):
"""
Reconstruit le chemin du départ à l'arrivée.
:param predecesseurs: (dict) Dictionnaire des prédécesseurs
:param depart: (str) Ville de départ
:param arrivee: (str) Ville d'arrivée
:return: (list) Chemin sous forme de liste de villes
"""
chemin = []
courant = arrivee
while courant is not None:
chemin.append(courant)
courant = predecesseurs[courant]
chemin.reverse()
# Vérifier que le chemin commence bien par le départ
if chemin[0] != depart:
return [] # Pas de chemin trouvé
return chemin
# =============================================================================
# PARTIE 4 : GPS Navigator
# =============================================================================
class GPSNavigator:
"""Système de navigation GPS."""
def __init__(self):
self.reseau = ReseauRoutier()
def charger_carte(self, donnees):
"""
Charge une carte depuis des données.
:param donnees: (dict) {"villes": [...], "routes": [...]}
"""
for ville in donnees.get("villes", []):
self.reseau.ajouter_ville(ville)
for route in donnees.get("routes", []):
self.reseau.ajouter_route(route[0], route[1], route[2])
def calculer_itineraire(self, depart, arrivee):
"""
Calcule l'itinéraire optimal entre deux villes.
:param depart: (str) Ville de départ
:param arrivee: (str) Ville d'arrivée
:return: (dict) Informations sur l'itinéraire
"""
if depart not in self.reseau.graphe:
return {"erreur": f"Ville inconnue : {depart}"}
if arrivee not in self.reseau.graphe:
return {"erreur": f"Ville inconnue : {arrivee}"}
distances, predecesseurs = dijkstra_avec_chemin(self.reseau, depart)
if distances[arrivee] == float('inf'):
return {"erreur": "Aucun itinéraire trouvé"}
chemin = reconstruire_chemin(predecesseurs, depart, arrivee)
return {
"depart": depart,
"arrivee": arrivee,
"distance": distances[arrivee],
"chemin": chemin,
"nb_etapes": len(chemin) - 1
}
def afficher_itineraire(self, itineraire):
"""Affiche l'itinéraire de manière lisible."""
if "erreur" in itineraire:
print(f"Erreur : {itineraire['erreur']}")
return
print(f"\n{'='*50}")
print(f"ITINÉRAIRE : {itineraire['depart']}{itineraire['arrivee']}")
print(f"{'='*50}")
print(f"Distance totale : {itineraire['distance']} km")
print(f"Nombre d'étapes : {itineraire['nb_etapes']}")
print(f"\nChemin :")
for i, ville in enumerate(itineraire['chemin']):
if i < len(itineraire['chemin']) - 1:
print(f" {i+1}. {ville}")
else:
print(f" {i+1}. {ville} (arrivée)")
print(f"{'='*50}\n")
def toutes_les_distances(self, depart):
"""
Affiche les distances vers toutes les villes depuis un point de départ.
:param depart: (str) Ville de départ
"""
distances = dijkstra(self.reseau, depart)
print(f"\nDistances depuis {depart} :")
for ville, dist in sorted(distances.items(), key=lambda x: x[1]):
if dist == float('inf'):
print(f" {ville} : inaccessible")
else:
print(f" {ville} : {dist} km")
# =============================================================================
# PARTIE 5 : Améliorations (Bonus)
# =============================================================================
class GPSNavigatorAvance(GPSNavigator):
"""GPS avec fonctionnalités avancées."""
def __init__(self):
super().__init__()
self.vitesses = {} # Vitesses moyennes par route
def ajouter_route_avec_vitesse(self, ville1, ville2, distance, vitesse):
"""Ajoute une route avec sa vitesse moyenne."""
self.reseau.ajouter_route(ville1, ville2, distance)
cle = tuple(sorted([ville1, ville2]))
self.vitesses[cle] = vitesse
def calculer_temps(self, ville1, ville2, distance):
"""Calcule le temps de trajet en minutes."""
cle = tuple(sorted([ville1, ville2]))
vitesse = self.vitesses.get(cle, 50) # 50 km/h par défaut
return (distance / vitesse) * 60 # En minutes
def calculer_itineraire_via(self, depart, arrivee, via):
"""
Calcule un itinéraire passant par des points intermédiaires.
:param via: (list) Liste des villes intermédiaires dans l'ordre
"""
etapes = [depart] + via + [arrivee]
chemin_complet = []
distance_totale = 0
for i in range(len(etapes) - 1):
itineraire = self.calculer_itineraire(etapes[i], etapes[i + 1])
if "erreur" in itineraire:
return itineraire
if chemin_complet:
chemin_complet.extend(itineraire['chemin'][1:])
else:
chemin_complet = itineraire['chemin']
distance_totale += itineraire['distance']
return {
"depart": depart,
"arrivee": arrivee,
"via": via,
"distance": distance_totale,
"chemin": chemin_complet,
"nb_etapes": len(chemin_complet) - 1
}
# =============================================================================
# PARTIE 6 : Réponses aux questions
# =============================================================================
"""
RÉPONSES AUX QUESTIONS DE SYNTHÈSE
1. Complexité temporelle de Dijkstra :
- Avec notre implémentation : O(V²) où V est le nombre de sommets
- La recherche du minimum parcourt tous les sommets non visités
- Pour chaque sommet, on parcourt ses voisins
2. Amélioration pour les grands graphes :
- Utiliser un tas binaire (heap) pour la sélection du minimum : O((V+E)log V)
- Utiliser un tas de Fibonacci : O(E + V log V)
- Utiliser A* (A-star) qui est une variante avec heuristique
3. Pourquoi pas de poids négatifs :
- Dijkstra suppose qu'une fois un sommet visité, sa distance est définitive
- Un poids négatif pourrait permettre de trouver un chemin plus court
en passant par un sommet déjà visité
- Pour les poids négatifs, utiliser Bellman-Ford
4. Chemin passant par tous les sommets :
- C'est le problème du voyageur de commerce (TSP)
- Problème NP-complet, pas de solution polynomiale connue
- On utilise des heuristiques ou des algorithmes d'approximation
"""
# =============================================================================
# TESTS
# =============================================================================
if __name__ == "__main__":
print("=" * 60)
print("TEST 1 : CRÉATION DU RÉSEAU")
print("=" * 60)
gps = GPSNavigator()
# Ajout des villes de la Côte d'Azur
villes = ["Nice", "Monaco", "Menton", "Cannes", "Antibes", "Grasse", "Vence"]
for ville in villes:
gps.reseau.ajouter_ville(ville)
# Ajout des routes (distances en km)
routes = [
("Nice", "Monaco", 20),
("Monaco", "Menton", 10),
("Nice", "Cannes", 33),
("Nice", "Antibes", 23),
("Cannes", "Antibes", 11),
("Cannes", "Grasse", 17),
("Nice", "Grasse", 40),
("Nice", "Vence", 22),
("Grasse", "Vence", 15)
]
for v1, v2, dist in routes:
gps.reseau.ajouter_route(v1, v2, dist)
print("\nRéseau routier :")
gps.reseau.afficher()
print("\n" + "=" * 60)
print("TEST 2 : DIJKSTRA SIMPLE")
print("=" * 60)
distances = dijkstra(gps.reseau, "Nice")
print("\nDistances depuis Nice :")
for ville, dist in sorted(distances.items(), key=lambda x: x[1]):
print(f" {ville} : {dist} km")
print("\n" + "=" * 60)
print("TEST 3 : CALCUL D'ITINÉRAIRE")
print("=" * 60)
itineraire = gps.calculer_itineraire("Menton", "Grasse")
gps.afficher_itineraire(itineraire)
itineraire2 = gps.calculer_itineraire("Monaco", "Vence")
gps.afficher_itineraire(itineraire2)
itineraire3 = gps.calculer_itineraire("Cannes", "Menton")
gps.afficher_itineraire(itineraire3)
print("\n" + "=" * 60)
print("TEST 4 : TOUTES LES DISTANCES")
print("=" * 60)
gps.toutes_les_distances("Cannes")
print("\n" + "=" * 60)
print("TEST 5 : ITINÉRAIRE VIA (BONUS)")
print("=" * 60)
gps_avance = GPSNavigatorAvance()
for ville in villes:
gps_avance.reseau.ajouter_ville(ville)
for v1, v2, dist in routes:
gps_avance.reseau.ajouter_route(v1, v2, dist)
itineraire_via = gps_avance.calculer_itineraire_via("Menton", "Cannes", via=["Nice"])
print(f"\nItinéraire Menton → Nice → Cannes :")
print(f" Distance : {itineraire_via['distance']} km")
print(f" Chemin : {''.join(itineraire_via['chemin'])}")

View File

@@ -28,7 +28,7 @@ Lacheminement efficace des données est crucial pour garantir la rapidité, l
### Mais dis moi Jamy, qu'est ce qu'un routeur ?
### Mais dis-moi Jamy, qu'est-ce qu'un routeur ?
@@ -36,7 +36,7 @@ Lacheminement efficace des données est crucial pour garantir la rapidité, l
Un routeur est en quelque chose un hôte, un ordinateur, qui possède deux périphériques réseau, qui disposent d'une adresse IP chacun.
Un routeur est en quelque sorte un hôte, un ordinateur, qui possède deux périphériques réseau, qui disposent d'une adresse IP chacun.
Lorsque des données transitent sur le réseau, chaque routeur va en recevoir, et pourra décider de les transmettre à telle ou telle machine.
Pour ne pas se tromper et faire le bon choix, le routeur se servira de **tables de routage.**
@@ -91,13 +91,13 @@ Comme on peut le voir, on dispose de plusieurs informations :
Depuis le routeur X, pour atteindre une destination, on va indiquer par quel routeur on passera, et la distance / le coût pour atteindre notre destination.
On parle alors de **passerelle** quand un routeur permet d'accéder à un sous-réseau différent du notre.
On parle alors de **passerelle** quand un routeur permet d'accéder à un sous-réseau différent du nôtre.
### Les algorithmes de routage
Il paraît évident qu'un réseau aussi grand qu'internet ne peut pas reposer sur des tables de routages manuelles. On a donc décidé d'automatiser cette tâche à l'aide d'algorithmes.
Il paraît évident qu'un réseau aussi grand qu'Internet ne peut pas reposer sur des tables de routage manuelles. On a donc décidé d'automatiser cette tâche à l'aide d'algorithmes.
Ainsi, ceux-ci permettent aux routeurs de choisir le chemin optimal pour transmettre les données. On distingue deux grandes catégories :
- **Les algorithmes à état de liens (Link-State)** : Chaque routeur construit une carte du réseau et calcule les meilleurs chemins avec des algorithmes comme **Dijkstra**.
@@ -108,11 +108,11 @@ Ainsi, ceux-ci permettent aux routeurs de choisir le chemin optimal pour transme
Ici, chaque routeur va attribuer une distance la plus courte possible à chaque destination accessible.
Cette distance sera indiquée en unité de sauts (hop), avec un saut = un routeur traversé.
Pour commencer, chaque routeur va écrire dans sa table les plus proches voisins, c'est à dire les routeurs accessibles à 0 de distance.
Pour commencer, chaque routeur va écrire dans sa table les plus proches voisins, c'est-à-dire les routeurs accessibles à 1 saut de distance.
Puis, toutes les 30 secondes, chaque routeur va propager les informations de sa table vers ses voisins immédiats, qui mettront leur propre table à jour avec ces informations, notamment si un chemin plus court vers une destination existe !
Puis, toutes les 30 secondes, chaque routeur va propager les informations de sa table vers ses voisins immédiats, qui mettront leur propre table à jour avec ces informations, notamment si un chemin plus court vers une destination existe !
Puis que l'on dispose d'un routeur comme **direction**, et d'une **distance**, on a donc un **vecteur**.
Puisque l'on dispose d'un routeur comme **direction**, et d'une **distance**, on a donc un **vecteur**.
**Attention** !
@@ -123,11 +123,11 @@ Si un routeur (et donc un réseau) n'est plus accessible passé 3 minutes, il se
À retenir :
- Distance en terme de sauts (ou hop en anglais)
- Chaque routeur ne possède les informations que sur son voisinnage direct. On parle de ***routing by rumor*** : aucune vision globale du réseau, on fait confiance aux voisins pour savoir ce qu'il en est.
- Au delà de 15 sauts, on considère un routeur inaccessible.
- Chaque routeur ne possède les informations que sur son voisinage direct. On parle de ***routing by rumor*** : aucune vision globale du réseau, on fait confiance aux voisins pour savoir ce qu'il en est.
- Au-delà de 15 sauts, on considère un routeur inaccessible.
- Utilise lalgorithme à vecteur de distance de Bellman-Ford et est simple à configurer, mais limité en performance.
- Convient aux petits réseaux
- Premiere algorithme de routage de l'histoire
- Premier algorithme de routage de l'histoire
@@ -158,7 +158,7 @@ $$
où d est le débit nominal en bits/seconde.
- Chaque réseau pourra être représenté sous forme de graphe.
- On recherchera les routes les moins couteuses, et sans cycle.
- On recherchera les routes les moins coûteuses, et sans cycle.
- Chaque routeur devient alors la racine d'un arbre contenant les meilleurs routes.
@@ -177,7 +177,7 @@ Enfin, il reste un algorithme :
Que nous ne verrons pas cette année.
Merci jamy !
Merci Jamy !
![merci jamy](assets/jamy.png)

391
Routage/TP_GPS_Navigator.md Normal file
View File

@@ -0,0 +1,391 @@
# TP : GPS Navigator — Itinéraires optimaux
## Contexte
Vous êtes développeur chez une startup qui souhaite concurrencer Google Maps et Waze. Votre mission : implémenter le moteur de calcul d'itinéraires en utilisant l'algorithme de **Dijkstra** pour trouver les chemins les plus courts dans un réseau routier.
Saviez-vous que Google Maps effectue des milliards de calculs Dijkstra chaque jour ?
---
## Objectifs
- Modéliser un réseau routier sous forme de graphe
- Implémenter l'algorithme de Dijkstra
- Calculer les itinéraires optimaux (distance ou temps)
- Reconstruire le chemin complet entre deux points
---
## Partie 1 : Modélisation du réseau
### 1.1. Représentation d'un graphe
Un réseau routier peut être modélisé par un **graphe pondéré** :
- Les **sommets** représentent les intersections/villes
- Les **arêtes** représentent les routes
- Les **poids** représentent les distances ou temps de trajet
Nous utiliserons un dictionnaire d'adjacence :
```python
# Exemple : réseau simplifié de la Côte d'Azur
reseau = {
"Nice": [("Monaco", 20), ("Cannes", 33), ("Grasse", 40)],
"Monaco": [("Nice", 20), ("Menton", 10)],
"Cannes": [("Nice", 33), ("Grasse", 17), ("Antibes", 11)],
"Grasse": [("Nice", 40), ("Cannes", 17)],
"Antibes": [("Cannes", 11), ("Nice", 23)],
"Menton": [("Monaco", 10)]
}
```
### 1.2. Création du graphe
Implémenter une classe `ReseauRoutier` pour gérer le graphe :
```python
class ReseauRoutier:
"""Représente un réseau routier sous forme de graphe."""
def __init__(self):
"""Initialise un réseau vide."""
self.graphe = {}
def ajouter_ville(self, ville):
"""
Ajoute une ville (sommet) au réseau.
:param ville: (str) Nom de la ville
"""
# À compléter
pass
def ajouter_route(self, ville1, ville2, distance):
"""
Ajoute une route bidirectionnelle entre deux villes.
:param ville1: (str) Première ville
:param ville2: (str) Deuxième ville
:param distance: (int) Distance en km
"""
# À compléter
pass
def get_voisins(self, ville):
"""
Retourne la liste des voisins d'une ville avec les distances.
:param ville: (str) Nom de la ville
:return: (list) Liste de tuples (voisin, distance)
"""
# À compléter
pass
def afficher(self):
"""Affiche le réseau."""
for ville, voisins in self.graphe.items():
print(f"{ville} -> {voisins}")
```
**Test** :
```python
reseau = ReseauRoutier()
reseau.ajouter_ville("Nice")
reseau.ajouter_ville("Monaco")
reseau.ajouter_route("Nice", "Monaco", 20)
reseau.afficher()
# Nice -> [('Monaco', 20)]
# Monaco -> [('Nice', 20)]
```
---
## Partie 2 : Algorithme de Dijkstra
### 2.1. Principe de l'algorithme
L'algorithme de Dijkstra trouve le plus court chemin depuis une source vers tous les autres sommets :
1. Initialiser toutes les distances à l'infini, sauf la source à 0
2. Marquer tous les sommets comme non visités
3. Tant qu'il reste des sommets non visités :
- Sélectionner le sommet non visité avec la plus petite distance
- Pour chaque voisin, mettre à jour la distance si on trouve un chemin plus court
- Marquer le sommet comme visité
### 2.2. Implémentation
```python
def dijkstra(reseau, depart):
"""
Algorithme de Dijkstra pour trouver les plus courts chemins.
:param reseau: (ReseauRoutier) Le réseau routier
:param depart: (str) Ville de départ
:return: (dict) Distances minimales depuis le départ
"""
# Initialisation
distances = {ville: float('inf') for ville in reseau.graphe}
distances[depart] = 0
non_visites = set(reseau.graphe.keys())
# À compléter : boucle principale de l'algorithme
return distances
```
**Test** :
```python
>>> dijkstra(reseau, "Nice")
{'Nice': 0, 'Monaco': 20, 'Cannes': 33, 'Grasse': 40, 'Antibes': 44, 'Menton': 30}
```
### 2.3. Sélection du minimum
Implémenter une fonction auxiliaire pour sélectionner le sommet non visité avec la plus petite distance :
```python
def selectionner_minimum(distances, non_visites):
"""
Sélectionne le sommet non visité avec la distance minimale.
:param distances: (dict) Distances actuelles
:param non_visites: (set) Ensemble des sommets non visités
:return: (str) Le sommet avec la distance minimale
"""
# À compléter
pass
```
---
## Partie 3 : Reconstruction du chemin
### 3.1. Mémorisation des prédécesseurs
Pour reconstruire le chemin, il faut mémoriser par quel sommet on est passé. Modifier `dijkstra` pour retourner aussi les prédécesseurs :
```python
def dijkstra_avec_chemin(reseau, depart):
"""
Dijkstra avec mémorisation des prédécesseurs.
:return: (tuple) (distances, predecesseurs)
"""
distances = {ville: float('inf') for ville in reseau.graphe}
predecesseurs = {ville: None for ville in reseau.graphe}
distances[depart] = 0
non_visites = set(reseau.graphe.keys())
while non_visites:
courant = selectionner_minimum(distances, non_visites)
non_visites.remove(courant)
if distances[courant] == float('inf'):
break
for voisin, poids in reseau.get_voisins(courant):
nouvelle_distance = distances[courant] + poids
if nouvelle_distance < distances[voisin]:
distances[voisin] = nouvelle_distance
predecesseurs[voisin] = courant # Mémoriser le prédécesseur
return distances, predecesseurs
```
### 3.2. Reconstruction
Implémenter la fonction de reconstruction du chemin :
```python
def reconstruire_chemin(predecesseurs, depart, arrivee):
"""
Reconstruit le chemin du départ à l'arrivée.
:param predecesseurs: (dict) Dictionnaire des prédécesseurs
:param depart: (str) Ville de départ
:param arrivee: (str) Ville d'arrivée
:return: (list) Chemin sous forme de liste de villes
"""
# À compléter
pass
```
**Test** :
```python
>>> distances, pred = dijkstra_avec_chemin(reseau, "Nice")
>>> reconstruire_chemin(pred, "Nice", "Menton")
['Nice', 'Monaco', 'Menton']
```
---
## Partie 4 : GPS Navigator
### 4.1. Classe GPS
Créer une classe complète de navigation :
```python
class GPSNavigator:
"""Système de navigation GPS."""
def __init__(self):
self.reseau = ReseauRoutier()
def charger_carte(self, fichier):
"""Charge une carte depuis un fichier."""
# Bonus : à implémenter
pass
def calculer_itineraire(self, depart, arrivee):
"""
Calcule l'itinéraire optimal entre deux villes.
:param depart: (str) Ville de départ
:param arrivee: (str) Ville d'arrivée
:return: (dict) Informations sur l'itinéraire
"""
distances, predecesseurs = dijkstra_avec_chemin(self.reseau, depart)
if distances[arrivee] == float('inf'):
return {"erreur": "Aucun itinéraire trouvé"}
chemin = reconstruire_chemin(predecesseurs, depart, arrivee)
return {
"depart": depart,
"arrivee": arrivee,
"distance": distances[arrivee],
"chemin": chemin,
"nb_etapes": len(chemin) - 1
}
def afficher_itineraire(self, itineraire):
"""Affiche l'itinéraire de manière lisible."""
if "erreur" in itineraire:
print(itineraire["erreur"])
return
print(f"\n{'='*50}")
print(f"ITINÉRAIRE : {itineraire['depart']}{itineraire['arrivee']}")
print(f"{'='*50}")
print(f"Distance totale : {itineraire['distance']} km")
print(f"Nombre d'étapes : {itineraire['nb_etapes']}")
print(f"\nChemin :")
for i, ville in enumerate(itineraire['chemin']):
if i < len(itineraire['chemin']) - 1:
print(f" {i+1}. {ville} →")
else:
print(f" {i+1}. {ville} (arrivée)")
print(f"{'='*50}\n")
```
### 4.2. Test complet
```python
# Création du GPS
gps = GPSNavigator()
# Ajout des villes de la Côte d'Azur
villes = ["Nice", "Monaco", "Menton", "Cannes", "Antibes", "Grasse", "Vence"]
for ville in villes:
gps.reseau.ajouter_ville(ville)
# Ajout des routes (distances en km)
routes = [
("Nice", "Monaco", 20),
("Monaco", "Menton", 10),
("Nice", "Cannes", 33),
("Nice", "Antibes", 23),
("Cannes", "Antibes", 11),
("Cannes", "Grasse", 17),
("Nice", "Grasse", 40),
("Nice", "Vence", 22),
("Grasse", "Vence", 15)
]
for v1, v2, dist in routes:
gps.reseau.ajouter_route(v1, v2, dist)
# Calcul d'itinéraires
itineraire = gps.calculer_itineraire("Menton", "Grasse")
gps.afficher_itineraire(itineraire)
```
**Sortie attendue** :
```
==================================================
ITINÉRAIRE : Menton → Grasse
==================================================
Distance totale : 70 km
Nombre d'étapes : 3
Chemin :
1. Menton →
2. Monaco →
3. Nice →
4. Grasse (arrivée)
==================================================
```
---
## Partie 5 : Améliorations (Bonus)
### 5.1. Itinéraire par temps de trajet
Modifier le système pour calculer l'itinéraire le plus rapide au lieu du plus court :
- Stocker aussi la vitesse moyenne de chaque route
- Calculer le temps de trajet : `temps = distance / vitesse`
### 5.2. Points d'intérêt
Ajouter la possibilité de passer par des points intermédiaires :
```python
gps.calculer_itineraire_via("Nice", "Menton", via=["Monaco"])
```
### 5.3. Éviter certaines routes
Permettre d'éviter des routes (travaux, péages...) :
```python
gps.calculer_itineraire("Nice", "Cannes", eviter=["autoroute"])
```
### 5.4. Visualisation
Utiliser `matplotlib` ou `networkx` pour afficher graphiquement le réseau et l'itinéraire calculé.
---
## Partie 6 : Questions de synthèse
1. Quelle est la complexité temporelle de l'algorithme de Dijkstra avec notre implémentation ?
2. Comment améliorer les performances pour de très grands graphes (millions de sommets) ?
3. Pourquoi Dijkstra ne fonctionne-t-il pas avec des poids négatifs ?
4. Quel algorithme utiliseriez-vous pour trouver le chemin passant par tous les sommets exactement une fois ?
---
## Barème indicatif
| Partie | Points |
|--------|--------|
| Partie 1 : Modélisation | 3 |
| Partie 2 : Dijkstra | 6 |
| Partie 3 : Reconstruction | 4 |
| Partie 4 : GPS Navigator | 5 |
| Partie 5 : Bonus | 2 |
| **Total** | **20** |
---
Auteur : Florian Mathieu
Licence CC BY NC
<a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/"><img alt="Licence Creative Commons" style="border-width:0" src="https://i.creativecommons.org/l/by-nc-sa/4.0/88x31.png" /></a> <br />Ce cours est mis à disposition selon les termes de la <a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/4.0/">Licence Creative Commons Attribution - Pas d'Utilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International</a>.

View File

@@ -0,0 +1,55 @@
import networkx as nx
import matplotlib.pyplot as plt
import matplotlib.animation as animation
# Création du graphe
G = nx.DiGraph()
edges = [
(0, 1, 4), (0, 2, 1),
(2, 1, 2), (1, 3, 1),
(2, 3, 5), (3, 4, 3)
]
G.add_weighted_edges_from(edges)
# Position des nœuds pour affichage
pos = nx.spring_layout(G, seed=42)
labels = {n: str(n) for n in G.nodes()}
# Initialisation de la figure
fig, ax = plt.subplots(figsize=(6, 6))
# Algorithme de Dijkstra avec animation
def dijkstra_animation(start_node=0):
distances = {node: float('inf') for node in G.nodes()}
distances[start_node] = 0
visited = set()
steps = []
while len(visited) < len(G.nodes()):
min_node = min((node for node in distances if node not in visited), key=lambda x: distances[x])
visited.add(min_node)
for neighbor in G.neighbors(min_node):
new_distance = distances[min_node] + G[min_node][neighbor]["weight"]
if new_distance < distances[neighbor]:
distances[neighbor] = new_distance
steps.append((visited.copy(), [(min_node, v) for v in G.neighbors(min_node)]))
return steps
steps = dijkstra_animation()
def update(frame):
ax.clear()
visited, edges = steps[frame]
node_colors = ["green" if n in visited else "lightblue" for n in G.nodes()]
edge_colors = ["red" if (u, v) in edges else "black" for u, v in G.edges()]
nx.draw(G, pos, with_labels=True, labels=labels, node_size=800,
node_color=node_colors, edge_color=edge_colors, font_size=14, font_weight="bold", ax=ax)
edge_labels = {(u, v): G[u][v]["weight"] for u, v in G.edges()}
nx.draw_networkx_edge_labels(G, pos, edge_labels=edge_labels, font_size=12, ax=ax)
ani = animation.FuncAnimation(fig, update, frames=len(steps), repeat=False, interval=1000)
plt.show()

View File

@@ -24,13 +24,13 @@ def dijkstra(g, s):
res[IND[s]] = 0
a_faire = [e for e in g[0]]
while a_faire != []:
ind_courrant = select_min(res, a_faire)
sommet_courrant = g[0][ind_courrant]
a_faire.remove(sommet_courrant)
cout_courrant = res[ind_courrant]
ind_courant = select_min(res, a_faire)
sommet_courant = g[0][ind_courant]
a_faire.remove(sommet_courant)
cout_courant = res[ind_courant]
for l in g[1]:
if l[0]==sommet_courrant:
cout_tmp=l[2]+cout_courrant
if l[0]==sommet_courant:
cout_tmp=l[2]+cout_courant
if res[IND[l[1]]]>=cout_tmp:
res[IND[l[1]]] = cout_tmp
return res