C12 : Exercices

C12.E1 : Construire un chemin entre deux nœuds

On souhaite non seulement savoir s'il existe un chemin entre deux sommets d'un graphe, mais également connaitre ce chemin.

Pour cela, on va reprendre et adapter la fonction du "12.4 - II.1" et écrire une deuxième fonction sur le modèle du "12.2 - II.2".

• Dans la fonction du "12.2 - II.1", il s'agit de remplacer la liste des sommets déjà vus par un dictionnaire. Les clés seront les sommets déjà vus. La valeur d'une clé sera le sommet qui a permis d'atteindre la clé. Par ailleurs, la fonction recursive aura un paramètre de plus : le sommet de départ lors de l'appel de la fonction. Ce paramètre permettra de connaitre le sommet parent lorsqu'un sommet sera ajouté au dictionnaire. Lors du premier appel de la fonction, on donnera à ce paramètre la valeur None afin de distinguer le sommet de départ des autres.

• Il s'agit ensuite d'encapsuler la récursivité dans une autre fonction. Dans cette deuxième fonction, le dictionnaire construit par récusivité permettra de reconstruire un chemin en "remontant" les sommets de proche en proche du sommet d'arrivé au sommet de départ.
Dans le cas où il n'existe pas de chemin, la fonction doit renvoyer None.

Remarque : L'algorithme proposé ici ne renvoie pas le chemin le plus court !

A faire : compléter les deux fonctions ci-dessous, ainsi que le programme principale qui permet de tester ces fonctions.

def parcours_rec_prof(g, sommet, sommet_parent, dico_sommets_déja_vus):
    """
    """
    pass

def chemin(g, s1, s2):
    """
    Paramètres
        g (Graphe) : graphe dans lequel on recherche un chemin
        s1 : sommet de départ, qui doit être présent dans g
        s2 : sommet d'arrivé, qui doit être présent dans g
    Valeur renvoyée :
        None si aucun chemin n'existe ou une liste (list) des sommets qui permettent d'aller de s1 à s2
    """
    pass
...
from classeGraphe import *

def parcours_rec_prof(g, sommet, sommet_parent, dico_sommets_déja_vus):
    """
    """
    dico_sommets_déja_vus[sommet] = sommet_parent
    for voisin in g.liste_voisins(sommet):
        if voisin not in dico_sommets_déja_vus:
            parcours_rec_prof(g, voisin, sommet, dico_sommets_déja_vus)

def chemin(g, s1, s2):
    """
    Paramètres
        g (Graphe) : graphe dans lequel on recherche un chemin
        s1 : sommet de départ, qui doit être présent dans g
        s2 : sommet d'arrivé, qui doit être présent dans g
    Valeur renvoyée :
        None si aucun chemin n'existe ou une liste (list) des sommets qui permettent d'aller de s1 à s2
    """
    # construction du dictionnaire
    dico = {}
    parcours_rec_prof(g, s1, None, dico)
    # utilisation du dictionnaire
    if s2 in dico:
        s = s2
        ch = [s]
        parent_s = dico[s]
        while parent_s != None:
            ch = [parent_s] + ch
            parent_s = dico[parent_s]
        return ch
    else:
        return None

# ===== Programme principal =====
# Création du graphe c1
graphe_C1 = Graphe()
graphe_C1.ajout_sommet(1)
graphe_C1.ajout_sommet(2)
graphe_C1.ajout_sommet(3)
graphe_C1.ajout_sommet(4)
graphe_C1.ajout_sommet(5)
graphe_C1.ajout_arc(1,3)
graphe_C1.ajout_arc(1,2)
graphe_C1.ajout_arc(2,4)
graphe_C1.ajout_arc(2,5)
graphe_C1.ajout_arc(3,2)
graphe_C1.ajout_arc(4,5)
graphe_C1.ajout_arc(5,1)
# Création du graphe C2
graphe_C2 = Graphe()
graphe_C2.ajout_sommet(1)
graphe_C2.ajout_sommet(2)
graphe_C2.ajout_sommet(3)
graphe_C2.ajout_sommet(4)
graphe_C2.ajout_sommet(5)
graphe_C2.ajout_arc(1,3)
graphe_C2.ajout_arc(1,2)
graphe_C2.ajout_arc(2,5)
graphe_C2.ajout_arc(3,2)
graphe_C2.ajout_arc(4,2)
graphe_C2.ajout_arc(4,5)
graphe_C2.ajout_arc(5,1)
# Affichage de quelques chemins
print(chemin(graphe_C1, 1, 4))
print(chemin(graphe_C1, 3, 1))
print(chemin(graphe_C1, 1, 8))

print(chemin(graphe_C2, 1, 4))

C12.E2 : Parcours en profondeur d'abord, sans récursivité

Proposer une fonction itérative (non récursive) parcours_it_profondeur(g, sommet, liste_sommets_visites), équivalente à la fonction def parcours_rec_profondeur(g, sommet, liste_sommets_visites) du 12.3. On pourra pour cela utiliser une pile.

...
from classeGraphe import *


# ===== Programme principal =====
# Création du graphe c1
graphe_C1 = Graphe()
graphe_C1.ajout_sommet(1)
graphe_C1.ajout_sommet(2)
graphe_C1.ajout_sommet(3)
graphe_C1.ajout_sommet(4)
graphe_C1.ajout_sommet(5)
graphe_C1.ajout_arc(1,3)
graphe_C1.ajout_arc(1,2)
graphe_C1.ajout_arc(2,4)
graphe_C1.ajout_arc(2,5)
graphe_C1.ajout_arc(3,2)
graphe_C1.ajout_arc(4,5)
graphe_C1.ajout_arc(5,1)
# Création du graphe C2
graphe_C2 = Graphe()
graphe_C2.ajout_sommet(1)
graphe_C2.ajout_sommet(2)
graphe_C2.ajout_sommet(3)
graphe_C2.ajout_sommet(4)
graphe_C2.ajout_sommet(5)
graphe_C2.ajout_arc(1,3)
graphe_C2.ajout_arc(1,2)
graphe_C2.ajout_arc(2,5)
graphe_C2.ajout_arc(3,2)
graphe_C2.ajout_arc(4,2)
graphe_C2.ajout_arc(4,5)
graphe_C2.ajout_arc(5,1)
# Affichage de quelques chemins

C12.E3 : Chemin de longueur minimale

Dans cet exercice, on souhaite déterminer le plus court chemin entre deux sommets (sommet de départ et sommet d'arrivée) d'un graphe.

L'algorithme se découpe en deux parties.

• La première partie consiste à construire un dictionnaire dont les clés sont les sommets du graphe et les valeurs les parents de ces sommets. La construction de ce dictionnaire se fera avec un parcours en largeur d'abord, sur le modèle de la fonction parcours_it_larg(g,s0) du 12.5 en remplaçant le dictionnaire dico_dist par le dictionnaire dico_sommets. Le parcours en largeur permet d'être sûr que le chemin suivi pour atteindre chaque sommet du graphe depuis le sommet de départ est le plus court possible.

• La deuxième partie consiste à construire la liste des sommets pour aller du sommet de départ au sommet d'arrivée. Cela se fait à l'aide du dictionnaire précédent, en "remontant" les sommets de proche en proche du sommet d'arrivée jusqu'au sommet de départ.

A faire : compléter le code ci-dessous.

def chemin(g, s0, s1):
    """
    Paramètres
        g (Graphe) : graphe dans lequel on recherche un chemin
        s0 : sommet de départ, qui doit être présent dans g
        s1 : sommet d'arrivée, qui doit être présent dans g
    Valeur renvoyée
        None si aucun chemin n'existe ou une liste (list) des sommets qui permettent d'aller de s1 à s2
    """
    # ===== Construction du dictionnaire des sommets du graphe
    # à partir de s0 avec un parcours en largeur d'abord =====
    dico_sommets = {s0: None}
    # à compléter

    # ===== Création de la liste des sommets du chemin de s0 à s1 =====
    # à compléter
...
from classeGraphe import *

def chemin(g, s0, s1):
    """
    Paramètres
        g (Graphe) : graphe dans lequel on recherche un chemin
        s0 : sommet de départ, qui doit être présent dans g
        s1 : sommet d'arrivée, qui doit être présent dans g
    Valeur renvoyée
        None si aucun chemin n'existe ou une liste (list) des sommets qui permettent d'aller de s1 à s2
    """
    # ===== Construction du dictionnaire des sommets du graphe
    # à partir de s0 avec un parcours en largeur d'abord =====
    dico_sommets = {s0: None}
    liste_en_cours = [s0]
    liste_suivants = []
    while len(liste_en_cours) > 0:
        s = liste_en_cours.pop()
        for voisin in g.liste_voisins(s):
            if voisin not in dico_sommets:
                liste_suivants.append(voisin)
                dico_sommets[voisin] = s
        if len(liste_en_cours) == 0:
            liste_en_cours = list(liste_suivants)
            liste_suivants = []

    # ===== Création de la liste des sommets du chemin de s0 à s1 =====
    # utilisation du dictionnaire
    if s1 in dico_sommets:
        s = s1
        ch = [s]
        parent_s = dico_sommets[s]
        while parent_s != None:
            ch = [parent_s] + ch
            parent_s = dico_sommets[parent_s]
        return ch
    else:
        return None

# ===== Programme principal =====
# Création du graphe c1
graphe_C1 = Graphe()
graphe_C1.ajout_sommet(1)
graphe_C1.ajout_sommet(2)
graphe_C1.ajout_sommet(3)
graphe_C1.ajout_sommet(4)
graphe_C1.ajout_sommet(5)
graphe_C1.ajout_arc(1,3)
graphe_C1.ajout_arc(1,2)
graphe_C1.ajout_arc(2,4)
graphe_C1.ajout_arc(2,5)
graphe_C1.ajout_arc(3,2)
graphe_C1.ajout_arc(4,5)
graphe_C1.ajout_arc(5,1)
# Création du graphe C2
graphe_C2 = Graphe()
graphe_C2.ajout_sommet(1)
graphe_C2.ajout_sommet(2)
graphe_C2.ajout_sommet(3)
graphe_C2.ajout_sommet(4)
graphe_C2.ajout_sommet(5)
graphe_C2.ajout_arc(1,3)
graphe_C2.ajout_arc(1,2)
graphe_C2.ajout_arc(2,5)
graphe_C2.ajout_arc(3,2)
graphe_C2.ajout_arc(4,2)
graphe_C2.ajout_arc(4,5)
graphe_C2.ajout_arc(5,1)
# Affichage de quelques chemins
print(chemin(graphe_C1, 1, 4))
print(chemin(graphe_C1, 3, 1))
print(chemin(graphe_C1, 1, 8))

print(chemin(graphe_C2, 1, 4))