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
g
de typeGraphe
correspondant au graphe \(G\) et d'ajouter les arcs issus du sommet0
.On considérera dans la suite de l'exercice que la variable
g
décrit complètement et correctement le graphe \(G\) :1
g.decrire()
Graphe d'ordre 4 Sommet 0 -> 1 2 Sommet 1 -> 2 Sommet 2 -> 0 Sommet 3 -> 0
-
-
Écrire une méthode
degre
de la classeGraphe
qui étant donné un sommets
du grapheself
renvoie le nombre de successeurs des
dans le grapheself
.1 2 3 4
def degre(self, s): """ Graphe, int -> int Renvoie le degré (sortant) du sommet s """ pass
1 2
for s in [0, 1, 2, 3]: print(g.degre(s), end=' ')
2 1 1 1
-
Écrire une méthode
degre_max
de la classeGraphe
qui 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 """ pass
1
print(g.degre_max())
2
-
-
Compléter le code de la méthode
matrice
de la classeGraphe
qui 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)\) vautTrue
si 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éter
1 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]
gp
de typeGraphe
repré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
parcours2
suivante.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 vus
1
print(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écursivefusionner
qui étant donné deux listesl1
etl2
toutes les deux triées par ordre croissant renvoie correctement une listel
dont les éléments sont ceux del1
etl2
trié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
seuil
au hasard dans la liste. On divise la listel
en deux listes : la listel1
des éléments del
qui sont inférieurs ou égaux àseuil
, la listel2
des éléments del
qui sont supérieurs strictement àseuil
. - Régner :: trier récursivement les listes
l1
etl2
en des listesl1_triee
etl2_triee
. - Combiner :: fusionner les éléments de
l1_triee
,l2_triee
etseuil
en 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,
seuil
ne sera pas choisit de manière aléatoire. On prendra toujours le premier élément de la listel
commeseuil
. -
comme
seuil
est le premier élément de la listel
, on divisera directement la queue del
lors de l'étape "divison" du tri rapide. -
La liste
l
est 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
diviser
qui étant donné une listel
et un entierseuil
, renvoie deux listesl1
etl2
de telle sorte que les éléments del1
sont les éléments del
inférieurs ou égaux àseuil
, les éléments del2
sont les éléments del
strictement supérieurs àseuil
.On pourra au choix écrire une fonction itérative ou bien compléter le code de la fonction récursive
diviser
ci-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
combiner
qui é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
combiner
ci-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_rapide
qui trie récursivement la listel
de la manière suivante :- si
l
est la liste vide ou la liste constituée d'un seul élément on renvoiel
; - on divise la liste
queue(l)
en deux listesl1
etl2
en choisissant comme seuiltete(l)
; - on trie récursivement
l1
etl2
en des listesl1_triee
etl2_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 """ pass
1 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)
lorsquel
est la liste \((5, 1, 9, 4, 3, 5, 6)\). On n'indiquera que les appels récursifs àtri_rapide
que l'on abrègera entr
. Les valeurs de retour seront indiquées en rouge sur l'arbre. -
On admet que la fonction
diviser
effectue \(n\) opérations élémentaires lorsquel
est constituée de \(n\) éléments. On admet quecombiner
effectue dans le pire des cas \(n_1\) opérations élémentaires lorsque la listel1
est 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 listel
pour laquelle l'instructiontri_fusion
réalise un nombre d'opérations élémentaires de l'ordre de \(n^2\), lorsquel
est constituée de \(n\) éléments.Indication. On pourra chercher une liste
l
de 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
l
est 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.