Réseaux, tris et graphes
Graphes : implémentation et parcours¶
Tous les graphes considérés dans cet exercice seront des graphes orientés, dont les sommets sont des entiers numérotés de \(0\) à \(n - 1\) pour un certain entier \(n \in \mathbb{N}^{*}\) (\(n\) est alors appelé l'ordre du graphe).
On peut implémenter ces types de graphes à l'aide d'une liste d'adjacence adj. On stocke à l'indice i de adj la liste des successeurs du sommet i dans le graphe. On donne ci-dessous le code d'une classe Graphe dont le constructeur Graphe prend en argument un entier n et créé un graphe d'ordre n, initialement sans aucun arc.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | |
La variable g de type Graphe représente le graphe \(G\) ci-dessous.

1 | |
[[1, 2], [2], [0], [0]]
-
Donner les instructions python permettant d'instancier une variable
gde typeGraphecorrespondant au graphe \(G\) et d'ajouter les arcs issus du sommet0.On considérera dans la suite de l'exercice que la variable
gdécrit complètement et correctement le graphe \(G\) :1g.decrire()Graphe d'ordre 4 Sommet 0 -> 1 2 Sommet 1 -> 2 Sommet 2 -> 0 Sommet 3 -> 0 -
-
Écrire une méthode
degrede la classeGraphequi étant donné un sommetsdu grapheselfrenvoie le nombre de successeurs desdans le grapheself.1 2 3 4
def degre(self, s): """ Graphe, int -> int Renvoie le degré (sortant) du sommet s """ pass1 2
for s in [0, 1, 2, 3]: print(g.degre(s), end=' ')2 1 1 1 -
Écrire une méthode
degre_maxde la classeGraphequi renvoie le maximum des degrés (sortants) des sommets du grapheself(suposé non vide).1 2 3 4
def degre_max(self): """ Graphe -> int Renvoie le maximum des degrés (sortants) des sommets du graphe """ pass1print(g.degre_max())2
-
-
Compléter le code de la méthode
matricede la classeGraphequi renvoie la matrice d'adjacence du grapheself. On rappelle que si \(G\) est un graphe orienté d'ordre \(n\), la matrice d'adjacence du graphe \(G\) est la matrice de taille \(n\times n\) dont le coefficient \((i, j)\) vautTruesi et seulement si \(j\) est un successeur de \(i\) dans le graphe \(G\).1 2 3 4 5 6 7
def matrice(self): """ Graphe -> [[int]] Renvoie la matrice d'adjacence du graphe self """ # mat est une matrice de taille n × n (où n = self.ordre) mat = [[False for i in range(self.ordre)] for j in range(self.ordre)] # À compléter1 2
for ligne in g.matrice(): print(ligne)4. La variable[False, True, True, False] [False, False, True, False] [True, False, False, False] [True, False, False, False]gpde typeGraphereprésente le graphe \(G'\) ci-dessous.
Graphe d'ordre 9 Sommet 0 -> 5 7 Sommet 1 -> 8 Sommet 2 -> 4 5 6 Sommet 3 -> Sommet 4 -> Sommet 5 -> 1 2 6 8 Sommet 6 -> 2 Sommet 7 -> 3 6 Sommet 8 ->-
On donne le code python de la fonction
parcours1.1 2 3 4 5 6 7
def parcours1(G, depart, vus): """ Graphe, Sommet, [Sommet] -> [Sommet] """ vus.append(depart) for destination in G.voisins(depart): if not destination in vus: parcours1(G, destination, vus) return vusÀ quel type de parcours de graphe cela correspond-t-il ? Déterminer l'affichage réalisé par l'instruction
print(parcours1(gp, 0, [])). -
Un élève un peu maladroit écrit la fonction
parcours2suivante.1 2 3 4 5 6 7 8 9 10 11 12
def parcours2(G, depart): """ Graphe, int, [int] -> [int] """ vus = [] à_explorer = [] à_explorer.append(depart) while len(à_explorer) > 0: sommet = à_explorer.pop() vus.append(sommet) for destination in reversed(G.voisins(sommet)): if not destination in vus: à_explorer.append(destination) return vus1print(parcours3(gp, 0))[0, 5, 1, 8, 2, 4, 6, 6, 8, 7, 3]Quel type de parcours l'élève tente-t-il d'implémenter ?
Que constate-t-on ? Comment corriger ce problème ?
-
Donner la liste des sommets visités (ordonnée par ordre de visite) obtenue lors d'un parcours en largeur du graphe \(G'\) depuis le sommet \(0\).
-
Algorithmes de tri¶
On pourra utiliser dans cet exercice sans justification supplémentaire les fonctions permettant de manipuler les listes : creer_vide, est_vide, est_singleton, tete, queue, ajoute. On rappelle que l'instruction ajoute(l, x) renvoie sans effet de bord une liste dont le premier élément est x suivit de la liste des éléments de l.
-
La fonction récursive
fusionnerécrite en TP est incorrecte. Écrire une fonction récursivefusionnerqui étant donné deux listesl1etl2toutes les deux triées par ordre croissant renvoie correctement une listeldont les éléments sont ceux del1etl2triés par ordre croissant.1 2 3 4
def fusionner(l1, l2): """ Liste, Liste -> Liste l1 et l2 sont triées par ordre croissant Renvoie la liste des éléments de l1 et l2 triés par ordre croissant """1 2 3
affiche(l3) affiche(l4) affiche(fusionner(l3, l4))1 - 3 - x 5 - 6 - x 1 - 3 - 5 - 6 - x -
le tri rapide ou tri pivot (en anglais quicksort) est un algorithme de tri inventé par C.A.R. Hoare en 19613 et fondé sur la méthode de conception diviser pour régner. On peut en résumer le principe avec l'image suivante :

- Diviser :: choisir un élément dit
seuilau hasard dans la liste. On divise la listelen deux listes : la listel1des éléments delqui sont inférieurs ou égaux àseuil, la listel2des éléments delqui sont supérieurs strictement àseuil. - Régner :: trier récursivement les listes
l1etl2en des listesl1_trieeetl2_triee. - Combiner :: fusionner les éléments de
l1_triee,l2_trieeetseuilen une liste triée.
Précisions. Afin de simplifier l'écriture des fonctions implémentant le tri rapide, on utilisera les simplifications suivantes :
- dans cet exercice,
seuilne sera pas choisit de manière aléatoire. On prendra toujours le premier élément de la listelcommeseuil. -
comme
seuilest le premier élément de la listel, on divisera directement la queue dellors de l'étape "divison" du tri rapide. -
La liste
lest la liste \((5, 1, 9, 4, 3, 5, 6)\).- Expliquer pourquoi l'opération de division produit les listes \(l_1 = (1, 4, 3, 5)\) et \(l_2 = (9, 6)\).
- Donner les listes \(l_1'\) et \(l_2'\) obtenues à la fin de l'étape "régner".
- En déduire la liste \(l\) obtenue à la fin de l'étape "combiner".
-
Écrire une fonction
diviserqui étant donné une listelet un entierseuil, renvoie deux listesl1etl2de telle sorte que les éléments del1sont les éléments delinférieurs ou égaux àseuil, les éléments del2sont les éléments delstrictement supérieurs àseuil.On pourra au choix écrire une fonction itérative ou bien compléter le code de la fonction récursive
diviserci-dessous:1 2 3 4 5 6 7 8 9 10 11 12 13
def diviser(l, seuil): """ Liste, int -> Liste, Liste Diviser la liste l en deux listes : - l1 (éléments y de l tels que y <= seuil) - l2 (éléments y de l tels que y > seuil) """ if ...: return ... else: l1, l2 = diviser(queue(l), seuil) if ...: return ... else: return ...1 2 3 4
affiche(l2) l21, l22 = diviser(l2, 3) affiche(l21) affiche(l22)3 - 3 - 1 - 8 - 5 - 2 - 0 - x 3 - 3 - 1 - 2 - 0 - x 8 - 5 - x -
Écrire une fonction
combinerqui étant donné un entierx, une listel1(supposée triée et dont tous les éléments sont inférieurs ou égaux àx), une listel2(supposée triée et dont tous les éléments sont strictement supérieurs àx) renvoie la liste triée dont les éléments sont, ceux del1,x, et ceux del2.On pourra au choix écrire une fonction itérative ou bien compléter le code de la fonction récursive
combinerci-dessous:1 2 3 4 5 6 7 8 9 10 11
def combiner(l1, x, l2): """ Liste, int, Liste -> Liste l1 est triée, y <= x pour tout y appartenant à l1 l2 est triée, x < y pour tout y appartenant à l2 Renvoie une liste triée constituée des éléments de la liste l1, de ceux la liste l2 et de l'élément x """ if est_vide(l1): return ... else: reste = combiner(queue(l1), x, l2) return ...1 2 3
affiche(l5) affiche(l6) affiche(combiner(l5, 5, l6))1 - 3 - 4 - 5 - x 6 - 9 - x 1 - 3 - 4 - 5 - 5 - 6 - 9 - x -
Écrire une fonction
tri_rapidequi trie récursivement la listelde la manière suivante :- si
lest la liste vide ou la liste constituée d'un seul élément on renvoiel; - on divise la liste
queue(l)en deux listesl1etl2en choisissant comme seuiltete(l); - on trie récursivement
l1etl2en des listesl1_trieeetl2_triee; - on combine les listes
l1_triee,l2_triee, l'élémenttete(l)et on renvoie le résultat.
1 2 3 4
def tri_rapide(l): """ Liste -> Liste Trie la liste l """ pass1 2
affiche(l) affiche(tri_rapide(l))5 - 1 - 9 - 4 - 3 - 5 - 6 - x 1 - 3 - 4 - 5 - 5 - 6 - 9 - x - si
-
Dresser l'arbre d'appel de l'instruction
tri_rapide(l)lorsquelest la liste \((5, 1, 9, 4, 3, 5, 6)\). On n'indiquera que les appels récursifs àtri_rapideque l'on abrègera entr. Les valeurs de retour seront indiquées en rouge sur l'arbre. -
On admet que la fonction
divisereffectue \(n\) opérations élémentaires lorsquelest constituée de \(n\) éléments. On admet quecombinereffectue dans le pire des cas \(n_1\) opérations élémentaires lorsque la listel1est constituée de \(n_1\) éléments (ce nombre ne dépend pas de la taille de la listel2).-
Comment appelle-t-on la complexité de la fonction
diviser? -
Dans le pire des cas, la fonction
tri_fusionà une complexité quadratique en la taille de la listel. Donner sans justifier un exemple de listelpour laquelle l'instructiontri_fusionréalise un nombre d'opérations élémentaires de l'ordre de \(n^2\), lorsquelest constituée de \(n\) éléments.Indication. On pourra chercher une liste
lde telle sorte que l'on effectue un maximum d'appels dans le pire des cas de la fonctioncombiner. Un point bonus sera attribué si vous parvenez à expliquer votre réponse à l'aide d'un arbre d'appel. -
Lorsque
lest une liste contituée de \(n\) éléments, l'instructiontri_rapide(l)réalise "en moyenne" un nombre de l'ordre de \(n\log_2(n)\) opérations élémentaires. Comment appelle-t-on la complexité "en moyenne" de la fonctiontri_rapide?
-
- Diviser :: choisir un élément dit
Protocoles réseaux¶
Les adresses IP sont composées de 4 octets, soit 32 bits. Elles sont
notées X1.X2.X3.X4, où X1, X2, X3 et X4 sont les valeurs des 4
octets, convertis en notation décimale. La notation X1.X2.X3.X4/n
signifie que les \(n\) premiers bits de poids fort de l'adresse IP
représentent la partie "réseau", les bits suivants représentent la
partie "hôte".
On représente ci-dessous un réseau dans lequel \(R1\), \(R2\), \(R3\), \(R4\), \(R5\) et \(R6\) sont des routeurs. Le réseau local \(L1\) est relié au routeur \(R1\) et le réseau local \(L2\) est relié au routeur \(R6\). Par exemple le routeur \(R1\) appartient au réseau \(112.44.65.0/24\) et l'adresse de son interface sur ce réseau est \(112.44.65.32\).

On donne également des extraits de la table de routage des routeurs \(R1\) à \(R5\) dans le tableau suivant.

-
Un paquet part du réseau local \(L1\) à destination du réseau local \(L2\).
-
En utilisant l'extrait de la table de routage de \(R1\), vers quel routeur \(R1\) envoie-t-il ce paquet : \(R2\) ou \(R3\) ? Justifier.
-
À l'aide des extraits de tables de routage ci-dessus, nommer les routeurs traversés par ce paquet, lorsqu'il va du réseau \(L1\) au réseau \(L2\).
-
-
La liaison entre \(R1\) et \(R2\) est rompue.
-
Sachant que le réseau utilise le protocole RIP (distance en nombre de sauts) pour configurer les tables de routage, donner les deux chemins possibles que pourra suivre un paquet allant de \(L1\) vers \(L2\).
-
Dans les extraits de tables de routage ci-dessus, quelle(s) ligne(s) seront modifiées ? Préciser comment.
-
-
On a rétabli la liaison entre \(R1\) et \(R2\).
Pour tenir compte du débit des liaisons, on décide d'utiliser le protocole OSPF (distance liée au coût des liaisons), pour déterminer les tables de routage. Le coût des liaisons entre les routeurs est donné ci-dessous :
- \(R1-R2\) : 100
- \(R1-R3\) : 100
- \(R2-R4\) : 1
- \(R2-R5\) : 10
- \(R2-R6\) : 10
- \(R3-R4\) : 10
- \(R4-R5\) : 1
- \(R4-R6\) : 10
-
\(R5-R6\) : 1
-
Le coût \(C\) d'une liaison est donné ici par la formule \(C = \dfrac{10^9}{BP}\), où \(BP\) est la bande passante de la connexion en bps (bit par seconde). Sachant que la bande passante de la laison \(R2-R3\) est de 10Mbps, calculer le coût correspondant. Quelle est la bande passante de la laison \(R2-R4\) ?
-
Donner sans justifier le chemin parcouru par un paquet partant du réseau \(L1\) et arrivant au réseau \(L2\), en utilisant le protocole \(OSPF\). Préciser le coût de ce chemin.