Module mvc.models.graphe
Gestion des graphes
Expand source code
#!/usr/bin/python3
# -*- coding: utf8 -*-
# @author : Sébastien LOZANO
"""Gestion des graphes"""
# Pour les collections
import collections
# Pour les produits cartésiens
from itertools import product
# Pour les copies
import copy # noqa : F401
# Pour l'aléatoire
import random
# Pour la gestion des imports relatifs
import sys
import os
SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__))
sys.path.append(os.path.dirname(SCRIPT_DIR))
from models.noeud import Noeud # noqa: E402
from models.arc import Arc # noqa: E402
class Graphe:
"""La classe Graphe sert pour créer un graphe.
Attributs
---------
- nom : str - Identification du graphe par un nom.
- noeuds : list - Les noeuds du graphe stockés dans une list.
- arcs : list - Les arcs du graphe stockés dans une list.
"""
def __init__(self, nom: str, noeuds=None, arcs=None) -> None:
"""Constructeur
Tests
-----
>>> g1 = Graphe('graphe1')
>>> print(g1)
Graphe(nom=graphe1, noeuds=[], arcs=[])
>>> A = Noeud('A')
>>> B = Noeud('B')
>>> g1.addNoeud(A)
>>> g1.addNoeud(B)
>>> a1 = Arc('A-1->B', A, B, 1)
>>> a2 = Arc('A-2->B(i)', A, B, 2, True)
>>> g1.addArc(a1)
>>> g1.addArc(a2)
>>> print(g1)
Graphe(nom=graphe1, noeuds=['A', 'B'], arcs=['A-1->B', 'A-2->B(i)'])
"""
pass
assert isinstance(nom, str), "Le nom d'un graphe doit être un string."
assert isinstance(noeuds, list) or noeuds is None, "Les noeuds d'un graphe sont soit une liste soit None." # noqa: E501
assert isinstance(arcs, list) or arcs is None, "Les arcs d'un graphe sont soit une liste soit None." # noqa: E501
self.nom = nom
if noeuds is None:
self.noeuds = []
else:
self.noeuds = noeuds
if arcs is None:
self.arcs = []
else:
self.arcs = arcs
def __str__(self) -> None:
"""Représentation en chaine de caractères d'une instance de la classe Graphe.
Tests
-----
>>> g1 = Graphe('graphe1')
>>> print(g1)
Graphe(nom=graphe1, noeuds=[], arcs=[])
>>> A = Noeud('A')
>>> B = Noeud('B')
>>> g1.addNoeud(A)
>>> g1.addNoeud(B)
>>> a1 = Arc('A-1->B', A, B, 1)
>>> a2 = Arc('A-2->B(i)', A, B, 2, True)
>>> g1.addArc(a1)
>>> g1.addArc(a2)
>>> print(g1)
Graphe(nom=graphe1, noeuds=['A', 'B'], arcs=['A-1->B', 'A-2->B(i)'])
""" # noqa: E501
pass
noeuds = []
for noeud in self.noeuds:
noeuds.append(noeud.nom)
arcs = []
for arc in self.arcs:
arcs.append(arc.nom)
return "Graphe(nom={}, noeuds={}, arcs={})".format(
self.nom,
noeuds, arcs
)
# ================================================================
# =========== GETTERS ============================================
# ================================================================
def getNom(self) -> str:
"""Renvoie le nom du graphe.
Tests
-----
>>> g1 = Graphe('graphe1')
>>> g1.getNom()
'graphe1'
"""
pass
return self.nom
def getNoeuds(self) -> list:
"""Renvoie le tableau des noeuds du graphe.
Tests
-----
>>> g1 = Graphe('graphe1')
>>> n1 = Noeud('n1')
>>> n2 = Noeud('n2')
>>> n3 = Noeud('n3')
>>> g1.addNoeud(n1)
>>> g1.addNoeud(n2)
>>> g1.addNoeud(n3)
>>> for noeud in g1.getNoeuds():
... print(noeud)
...
Noeud(nom=n1, voisins=[], nbArcs=0, capital=0)
Noeud(nom=n2, voisins=[], nbArcs=0, capital=0)
Noeud(nom=n3, voisins=[], nbArcs=0, capital=0)
"""
pass
return self.noeuds
def getArcs(self) -> list:
"""Renvoie le tableau des arcs du graphe.
Tests
-----
>>> g1 = Graphe('graphe1')
>>> n1 = Noeud('n1')
>>> n2 = Noeud('n2')
>>> n3 = Noeud('n3')
>>> g1.addNoeud(n1)
>>> g1.addNoeud(n2)
>>> g1.addNoeud(n3)
>>> a1 = Arc('n1n2_0', n1, n2, 1)
>>> a2 = Arc('n1n2_1', n1, n2, 2)
>>> a3 = Arc('n1n3_0', n1, n3, 3)
>>> a4 = Arc('n2n3_0', n2, n3, 4)
>>> g1.addArc(a1)
>>> g1.addArc(a2)
>>> g1.addArc(a3)
>>> g1.addArc(a4)
>>> for arc in g1.getArcs():
... # On n'affiche que les noms des arcs
... print(arc.nom)
...
n1n2_0
n1n2_1
n1n3_0
n2n3_0
>>> for noeud in g1.getNoeuds():
... print(noeud)
...
Noeud(nom=n1, voisins=['n2', 'n3'], nbArcs=3, capital=0)
Noeud(nom=n2, voisins=['n1', 'n3'], nbArcs=3, capital=0)
Noeud(nom=n3, voisins=['n1', 'n2'], nbArcs=2, capital=0)
"""
pass
return self.arcs
# ================================================================
# =========== SETTERS ============================================
# ================================================================
def setNom(self, nouveauNom) -> str:
"""Redéfinit le nom du graphe.
Paramètres
----------
nouveauNom : str - Le nouveau nom du graphe
Tests
-----
>>> g1 = Graphe('graphe1')
>>> g1.getNom()
'graphe1'
>>> g1.setNom('new_graphe1')
>>> g1.getNom()
'new_graphe1'
"""
pass
assert isinstance(nouveauNom, str), "Le nom d'un graphe doit être un string." # noqa : E501
self.nom = nouveauNom
def setNoeuds(self, nouveauxNoeuds: list) -> None:
"""Définit le tableau des noeuds d'une instance de la classe Graphe.
Paramètres
----------
nouveauxNoeuds : list - Une liste d'instances de la classe Noeuds.
Remarque:
---------
On ne définit les noeuds d'un graphe que si le graphe n'a aucun noeud.
Tests
-----
>>> g1 = Graphe('graphe1')
>>> print(g1)
Graphe(nom=graphe1, noeuds=[], arcs=[])
>>> n1 = Noeud('n1')
>>> n2 = Noeud('n2')
>>> n3 = Noeud('n3')
>>> nouveauxNoeuds = [n1, n2, n3]
>>> g1.setNoeuds(nouveauxNoeuds)
>>> print(g1)
Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=[])
"""
pass
# Un booléen pour tester si tout est OK
allOk = True
for nouveauNoeud in nouveauxNoeuds:
assert nouveauNoeud not in self.noeuds, "On ne peut ajouter un noeud déjà présent." # noqa : E501
if nouveauNoeud in self.noeuds:
allOk = False
raise ValueError("On ne peut ajouter un noeud déjà présent.")
if allOk:
for noeud in nouveauxNoeuds:
self.addNoeud(noeud)
def setArcs(self, nouveauxArcs: list) -> None:
"""Définit/Ajoute au tableau des arcs d'une instance de la classe Graphe un ensemble de nouveaux arcs.
Paramètres
----------
nouveauxArcs : list - Une liste d'instances de la classe Arcs.
Tests
-----
>>> g1 = Graphe('graphe1')
>>> print(g1)
Graphe(nom=graphe1, noeuds=[], arcs=[])
>>> n1 = Noeud('n1')
>>> n2 = Noeud('n2')
>>> n3 = Noeud('n3')
>>> nouveauxNoeuds = [n1, n2, n3]
>>> g1.setNoeuds(nouveauxNoeuds)
>>> print(g1)
Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=[])
>>> a1 = Arc('n1n2_0', n1, n2, 1)
>>> a2 = Arc('n1n2_1', n1, n2, 2)
>>> a3 = Arc('n1n3_0', n1, n3, 3)
>>> a4 = Arc('n2n3_0', n2, n3, 4)
>>> nouveauxArcs = [a1, a2, a3, a4]
>>> g1.setArcs(nouveauxArcs)
>>> print(g1)
Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=['n1n2_0', 'n1n2_1', 'n1n3_0', 'n2n3_0'])
""" # noqa : E501
pass
# Un booléen pour tester si tout est OK
allOk = True
for nouvelArc in nouveauxArcs:
assert nouvelArc not in self.arcs, "On ne peut ajouter un arc déjà présent." # noqa : E501
if nouvelArc in self.getArcs():
allOk = False
raise ValueError("On ne peut ajouter un arc déjà présent.")
assert nouvelArc.noeudDebut in self.noeuds or nouvelArc.noeudFin in self.noeuds, "NouvelArc doit lier deux noeuds du graphe." # noqa : E501
if (nouvelArc.getNoeudDebut() not in self.getNoeuds() or
nouvelArc.getNoeudFin() not in self.getNoeuds()):
allOk = False
raise ValueError("NouvelArc doit lier deux noeuds du graphe.")
if allOk:
for arc in nouveauxArcs:
self.addArc(arc)
# ================================================================
# =========== OUTILS =============================================
# ================================================================
def addNoeud(self, noeudCandidat: Noeud) -> None:
"""Ajoute une instance de la classe Noeud au graphe.
Paramètres
----------
noeudCandidat : Noeud - Un noeud à ajouter au graphe.
Tests
-----
>>> g1 = Graphe('graphe1')
>>> print(g1)
Graphe(nom=graphe1, noeuds=[], arcs=[])
>>> n1 = Noeud('n1')
>>> g1.addNoeud(n1)
>>> print(g1)
Graphe(nom=graphe1, noeuds=['n1'], arcs=[])
"""
pass
assert isinstance(noeudCandidat, Noeud), "noeudCandidat doit être une instance de la classe Noeud." # noqa : E501
assert noeudCandidat not in self.noeuds, "Pas de doublon pour les noeuds d'une instance de Graphe." # noqa : E501
nomsDesNoeudsDuGraphe = []
for noeud in self.noeuds:
nomsDesNoeudsDuGraphe.append(noeud.nom)
assert noeudCandidat.nom not in nomsDesNoeudsDuGraphe, "Deux noeuds d'une instance de Graphe ne peuvent avoir le même nom." # noqa : E501
# Un booléen pour tester si tout est OK
allOk = True
# Ne pas utiliser les assertions pour valider les données
# car elles peuvent être désactivées.
# On lève donc une erreur en cas de souci
if self.isNoeudInGraphe(noeudCandidat):
allOk = False
raise ValueError('Pas de doublon pour les noeuds d\'une instance de Graphe.') # noqa : E501
if noeudCandidat.nom in nomsDesNoeudsDuGraphe:
allOk = False
raise ValueError('Deux noeuds d\'une instance de Graphe ne peuvent avoir le même nom.') # noqa : E501
if allOk:
self.noeuds.append(noeudCandidat)
def isNoeudInGraphe(self, noeudCandidat: Noeud) -> bool:
"""Renvoie un booléen selon que l'instance de Noeud est dans le graphe ou non.
Paramètres
----------
noeudCandidat : Noeud - Une instance de la classe Noeud dont on veut savoir si elle appartient au graphe.
Tests
-----
>>> g1 = Graphe('graphe1')
>>> n1,n2 = Noeud('n1'),Noeud('n2')
>>> g1.addNoeud(n1)
>>> g1.isNoeudInGraphe(n1)
True
>>> g1.isNoeudInGraphe(n2)
False
""" # noqa : E501
pass
assert isinstance(noeudCandidat, Noeud), "Le noeudCandidat doit être un instance de la classe Noeud." # noqa : E501
isNoeudInGraphe = False
if noeudCandidat in self.getNoeuds():
isNoeudInGraphe = True
return isNoeudInGraphe
def hasNoeud(self) -> bool:
"""Renvoie un booléen selon que la propriété noeuds de l'instance de la classe Graphe
est une liste vide ou non.
Tests
-----
>>> g1 = Graphe('graphe1')
>>> n1 = Noeud('n1')
>>> g1.hasNoeud()
False
>>> g1.addNoeud(n1)
>>> g1.hasNoeud()
True
""" # noqa : E501
pass
hasNoeud = False
if self.getNoeuds():
hasNoeud = True
return hasNoeud
def addArc(self, arcCandidat: Arc) -> None:
"""Ajoute une instance de la classe Arc au graphe.
Paramètres
----------
arcCandidat : Arc - Un arc à ajouter au graphe.
Tests
-----
>>> g1 = Graphe('graphe1')
>>> print(g1)
Graphe(nom=graphe1, noeuds=[], arcs=[])
>>> n1 = Noeud('n1')
>>> n2 = Noeud('n2')
>>> g1.addNoeud(n1)
>>> g1.addNoeud(n2)
>>> print(g1)
Graphe(nom=graphe1, noeuds=['n1', 'n2'], arcs=[])
>>> arc = Arc('n1n2', n1, n2, 7)
>>> g1.addArc(arc)
>>> print(g1)
Graphe(nom=graphe1, noeuds=['n1', 'n2'], arcs=['n1n2'])
"""
pass
assert isinstance(arcCandidat, Arc), "arc doit être une instance de la classe Arc." # noqa : E501
assert arcCandidat not in self.arcs, "Pas de doublon pour les arcs d'une instance de Graphe." # noqa : E501
nomsDesArcsDuGraphe = []
for arc in self.getArcs():
nomsDesArcsDuGraphe.append(arc.nom)
assert arcCandidat.nom not in nomsDesArcsDuGraphe,"Deux arcs d'une instance de Graphe ne peuvent avoir le même nom." # noqa : E501
# Un booléen pour tester si tout est OK
allOk = True
# Ne pas utiliser les assertions pour valider les données
# car elles peuvent être désactivées.
# On lève donc une erreur en cas de souci
if arcCandidat in self.getArcs():
allOk = False
raise ValueError('Pas de doublon pour les arcs d\'une instance de Graphe.') # noqa : E501
if arcCandidat.nom in nomsDesArcsDuGraphe:
allOk = False
raise ValueError('Deux arcs d\'une instance de Graphe ne peuvent avoir le même nom.') # noqa : E501
if allOk:
self.arcs.append(arcCandidat)
def addArcs(self, arcsToAdd: list) -> None:
"""Ajoute une liste d'instances de la classe Arc au graphe.
Paramètres
----------
arcsToAdd : list - Une liste d'arcs à ajouter au graphe.
Tests
-----
>>> g = Graphe('graphe')
>>> print(g)
Graphe(nom=graphe, noeuds=[], arcs=[])
>>> A,B = Noeud('A'), Noeud('B')
>>> g.setNoeuds([A,B])
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B'], arcs=[])
>>> AB1 = Arc('A->B(1)', A, B, 7)
>>> g.addArc(AB1)
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B'], arcs=['A->B(1)'])
>>> AB2,AB3 = Arc('A->B(2)', A, B, 7),Arc('A->B(3)', A, B, 7)
>>> g.addArcs([AB2,AB3])
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B'], arcs=['A->B(1)', 'A->B(2)', 'A->B(3)'])
""" # noqa : E501
pass
for arc in arcsToAdd:
assert isinstance(arc, Arc), "arc doit être une instance de la classe Arc." # noqa : E501
for arc in arcsToAdd:
self.addArc(arc)
def removeArc(self, arcCandidat: Arc) -> None:
"""Supprime une instance de la classe Arc du graphe.
Paramètres
----------
arcCandidat : Arc - Un arc à supprimer du graphe.
Tests
-----
>>> g1 = Graphe('graphe1')
>>> print(g1)
Graphe(nom=graphe1, noeuds=[], arcs=[])
>>> n1,n2,n3 = Noeud('n1'),Noeud('n2'),Noeud('n3')
>>> g1.setNoeuds([n1,n2,n3])
>>> print(g1)
Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=[])
>>> a1 = Arc('n1n2_1', n1, n2, 1)
>>> a2 = Arc('n1n2_2', n1, n2, 2)
>>> a3 = Arc('n1n3', n1, n3, 3)
>>> g1.setArcs([a1,a2,a3])
>>> print(g1)
Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=['n1n2_1', 'n1n2_2', 'n1n3'])
>>> g1.removeArc(a1)
>>> print(g1)
Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=['n1n2_2', 'n1n3'])
>>> for n in g1.getNoeuds():
... print(n)
...
Noeud(nom=n1, voisins=['n2', 'n3'], nbArcs=2, capital=0)
Noeud(nom=n2, voisins=['n1'], nbArcs=1, capital=0)
Noeud(nom=n3, voisins=['n1'], nbArcs=1, capital=0)
>>> g1.removeArc(a3)
>>> for n in g1.getNoeuds():
... print(n)
...
Noeud(nom=n1, voisins=['n2'], nbArcs=1, capital=0)
Noeud(nom=n2, voisins=['n1'], nbArcs=1, capital=0)
Noeud(nom=n3, voisins=[], nbArcs=0, capital=0)
""" # noqa : E501
pass
assert isinstance(arcCandidat, Arc), "arc doit être une instance de la classe Arc." # noqa : E501
assert self.isArcInGraphe(arcCandidat), "L'arc à supprimer doit faire partie des arcs du graphe." # noqa : E501
# Un booléen pour tester si tout est OK
allOk = True
# Ne pas utiliser les assertions pour valider les données
# car elles peuvent être désactivées.
# On lève donc une erreur en cas de souci
if not self.isArcInGraphe(arcCandidat):
allOk = False
raise ValueError("L'arc à supprimer doit faire partie des arcs du graphe.") # noqa : E501
if allOk:
self.getArcs().remove(arcCandidat)
# Après suppression de l'arcCandidat,
# Si il n'y a plus d'arcs entre eux
# On met à jour le tableau des voisins des Noeuds
# Sinon, on gère uniquement les nombres d'arcs des Noeuds !
noeudDebut = arcCandidat.getNoeudDebut()
noeudFin = arcCandidat.getNoeudFin()
if ((noeudDebut not in self.getDebiteurs(noeudFin)) and
(noeudDebut not in self.getCreanciers(noeudFin))):
noeudDebut.removeVoisin(noeudFin)
else:
noeudDebut.setNbArcs(noeudDebut.getNbArcs() - 1)
noeudFin.setNbArcs(noeudFin.getNbArcs() - 1)
def isArcInGraphe(self, arcCandidat: Arc) -> bool:
"""Renvoie un booléen selon que l'instance de Arc est dans le graphe ou non.
Paramètres
----------
arcCandidat : Arc - Une instance de la classe Arc dont on veut savoir si elle appartient au graphe.
Tests
-----
>>> g1 = Graphe('graphe1')
>>> n1,n2,n3 = Noeud('n1'),Noeud('n2'),Noeud('n3')
>>> g1.setNoeuds([n1,n2,n3])
>>> a1,a2 = Arc('n1n2',n1,n2,1), Arc('n1n3',n1,n3,2)
>>> g1.addArc(a1)
>>> g1.isArcInGraphe(a1)
True
>>> g1.isArcInGraphe(a2)
False
""" # noqa : E501
pass
assert isinstance(arcCandidat, Arc), "arcCandidat doit être un instance de la classe Arc." # noqa : E501
isArcInGraphe = False
if arcCandidat in self.getArcs():
isArcInGraphe = True
return isArcInGraphe
def hasArc(self) -> bool:
"""Renvoie un booléen selon que la propriété arcs de l'instance de la classe Graphe
est une liste vide ou non.
Tests
-----
>>> g1 = Graphe('graphe1')
>>> n1,n2 = Noeud('n1'),Noeud('n2')
>>> arc = Arc('n1n2',n1,n2,1)
>>> g1.hasArc()
False
>>> g1.addNoeud(n1)
>>> g1.addNoeud(n2)
>>> g1.addArc(arc)
>>> g1.hasArc()
True
""" # noqa : E501
pass
hasArc = False
if self.getArcs():
hasArc = True
return hasArc
def getEntreprises(self) -> list:
"""Récupère dans une liste tous les noeuds du graphe, qui sont les entrepises
de la situation problème étudiée.
Tests
-----
>>> g1 = Graphe('graphe1')
>>> g1.getEntreprises()
[]
>>> n1,n2,n3 = Noeud('n1'),Noeud('n2'),Noeud('n3')
>>> g1.setNoeuds([n1,n2,n3])
>>> # On affiche uniquement les noms des noeuds pour le doctest
>>> for noeud in g1.getEntreprises():
... print(noeud.nom)
...
n1
n2
n3
""" # noqa : E501
pass
return self.getNoeuds()
def getDebiteurs(self, noeudCandidat: Noeud) -> list:
"""Récupére dans une liste, tous les noeuds débiteurs du noeudCandidat.
Paramètres
----------
noeudCandidat : Noeud - Une instance de la classe Noeud dont on veut les débiteurs.
Tests
-----
>>> g1 = Graphe('graphe1')
>>> n1,n2,n3,n4 = Noeud('n1'),Noeud('n2'),Noeud('n3'),Noeud('n4')
>>> g1.setNoeuds([n1,n2,n3,n4])
>>> a1,a2,a3,a4 = Arc('n2n1',n2,n1,1),Arc('n3n1',n3,n1,2),Arc('n4n1',n4,n1,3),Arc('n1n4',n1,n4,4)
>>> g1.setArcs([a1,a2,a3,a4])
>>> # On affiche uniquement les noms des noeuds débiteurs pour le doctest
>>> for noeud in g1.getDebiteurs(n1):
... print(noeud.nom)
...
n2
n3
n4
>>> for noeud in g1.getDebiteurs(n2):
... print(noeud.nom)
...
>>> for noeud in g1.getDebiteurs(n4):
... print(noeud.nom)
...
n1
""" # noqa : E501
pass
assert self.isNoeudInGraphe(noeudCandidat), "noeudCandidat doit être un noeud du graphe." # noqa : E501
# Ne pas utiliser les assertions pour valider les données
# car elles peuvent être désactivées.
# On lève donc une erreur en cas de souci
if not self.isNoeudInGraphe(noeudCandidat):
raise ValueError("noeudCandidat doit être un noeud du graphe.")
else:
noeudsDebiteurs = []
for arc in self.getArcs():
if arc.getNoeudFin() == noeudCandidat:
noeudsDebiteurs.append(arc.getNoeudDebut())
return noeudsDebiteurs
def getCreanciers(self, noeudCandidat: Noeud) -> list:
"""Récupére dans une liste, tous les noeuds créanciers du noeudCandidat.
Paramètres
----------
noeudCandidat : Noeud - Une instance de la classe Noeud dont on veut les créanciers.
Tests
-----
>>> g1 = Graphe('graphe1')
>>> n1,n2,n3,n4,n5 = Noeud('n1'),Noeud('n2'),Noeud('n3'),Noeud('n4'),Noeud('n5')
>>> g1.setNoeuds([n1,n2,n3,n4,n5])
>>> a1,a2,a3,a4 = Arc('n2n1',n2,n1,1),Arc('n3n1',n3,n1,2),Arc('n4n1',n4,n1,3),Arc('n1n4',n1,n4,4)
>>> a5,a6 = Arc('n1n3',n1,n3,5),Arc('n1n5',n1,n5,6)
>>> g1.setArcs([a1,a2,a3,a4,a5,a6])
>>> # On affiche uniquement les noms des noeuds créanciers pour le doctest
>>> for noeud in g1.getCreanciers(n1):
... print(noeud.nom)
...
n4
n3
n5
>>> for noeud in g1.getCreanciers(n2):
... print(noeud.nom)
...
n1
>>> for noeud in g1.getCreanciers(n5):
... print(noeud.nom)
...
""" # noqa : E501
pass
assert self.isNoeudInGraphe(noeudCandidat), "noeudCandidat doit être un noeud du graphe." # noqa : E501
# Ne pas utiliser les assertions pour valider les données
# car elles peuvent être désactivées.
# On lève donc une erreur en cas de souci
if not self.isNoeudInGraphe(noeudCandidat):
raise ValueError("noeudCandidat doit être un noeud du graphe.")
else:
noeudsCreanciers = []
for arc in self.getArcs():
if arc.getNoeudDebut() == noeudCandidat:
noeudsCreanciers.append(arc.getNoeudFin())
return noeudsCreanciers
def positionNette(
self,
noeudCandidat: Noeud,
algo1NomEntreprise=None,
algo1Don=0
) -> int:
"""Retourne la balance entre dettes et créances d'une instance de la classe Noeud.
Paramètres
----------
- noeudCandidat : Noeud - Une instance de la classe Noeud dont on veut les créanciers.
- algo1NomEntreprise : str - Le nom de l'entreprise choisie pour le don dans l'algorithme 1 - méthode heuristique
- algo1Don : int - Une valeur correspondant au don dans l'algorithme 1 méthode heuristique
Tests
-----
>>> g1 = Graphe('graphe1')
>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
>>> # arcs partant de A
>>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
>>> # arcs arrivant sur A
>>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
>>> # arc entre B et C
>>> BC = Arc('BC',B,C,5)
>>> g1.setNoeuds([A,B,C])
>>> g1.setArcs([AB1,AB2,AC,CA1,CA2,BC])
>>> g1.positionNette(A)
-1
>>> g1.positionNette(A,'A',5)
-6
>>> g1.positionNette(B)
2
>>> g1.positionNette(B,'A',5)
2
>>> g1.positionNette(C)
-1
""" # noqa : E501
pass
assert self.isNoeudInGraphe(noeudCandidat), "noeudCandidat doit être un noeud du graphe." # noqa : E501
# Ne pas utiliser les assertions pour valider les données
# car elles peuvent être désactivées.
# On lève donc une erreur en cas de souci
if not self.isNoeudInGraphe(noeudCandidat):
raise ValueError("noeudCandidat doit être un noeud du graphe.")
else:
# Total des sommes dues aux créanciers du noeudCandidat
detteTotaleDuNoeud = 0
# Total des sommes dues par les débiteurs du noeudCandidat
creanceTotaleDuNoeud = 0
debiteurs = self.getDebiteurs(noeudCandidat)
creanciers = self.getCreanciers(noeudCandidat)
for arc in self.getArcs():
noeudDebut = arc.getNoeudDebut()
noeudFin = arc.getNoeudFin()
if noeudDebut == noeudCandidat and noeudFin in creanciers:
detteTotaleDuNoeud += arc.poids
if noeudDebut in debiteurs and noeudFin == noeudCandidat:
creanceTotaleDuNoeud += arc.poids
if noeudCandidat.getNom() == algo1NomEntreprise:
positionNetteDuNoeud = (noeudCandidat.getCapital()
+ creanceTotaleDuNoeud
- detteTotaleDuNoeud
- algo1Don)
else:
positionNetteDuNoeud = (noeudCandidat.getCapital()
+ creanceTotaleDuNoeud
- detteTotaleDuNoeud)
return positionNetteDuNoeud
def detteGlobale(self) -> int:
"""Retourne la dette globale du graphe, c'est à dire la somme des dettes des entreprises.
Tests
-----
>>> g1 = Graphe('graphe1')
>>> E,D,F = Noeud('E'),Noeud('D'),Noeud('F')
>>> ED,DF,FE = Arc('ED',E,D,10),Arc('DF',D,F,7),Arc('FE',F,E,2)
>>> g1.setNoeuds([E,D,F])
>>> g1.setArcs([ED,DF,FE])
>>> g1.detteGlobale()
19
>>> g2 = Graphe('graphe2')
>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
>>> # arcs partant de A
>>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3)
>>> # arcs arrivant sur A
>>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1)
>>> # arc entre B et C
>>> BC = Arc('BC',B,C,5)
>>> g2.setNoeuds([A,B,C])
>>> g2.setArcs([AB1,AB2,AC,CA1,CA2,BC])
>>> g2.detteGlobale()
24
""" # noqa : E501
pass
assert len(self.getNoeuds()) >= 2, "Le graphe doit avoir au moins deux noeuds." # noqa : E501
# Ne pas utiliser les assertions pour valider les données
# car elles peuvent être désactivées.
# On lève donc une erreur en cas de souci
allOk = True
if len(self.getNoeuds()) < 2:
allOk = False
raise ValueError("Le graphe doit avoir au moins deux noeuds.")
if allOk:
detteGlobale = 0
for arc in self.getArcs():
detteGlobale += arc.poids
return detteGlobale
def simplification(self) -> None:
"""Modifie l'instance du graphe en remplaçant pour chaque couple d'entreprises A,B,
l'ensemble des dettes de A vers B par une seule dette égale à la somme des dettes.
Tests
-----
>>> g = Graphe('graphe')
>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
>>> AB2 = Arc('AB2',A,B,2)
>>> BC7,BC4 = Arc('BC7',B,C,7),Arc('BC4',B,C,4)
>>> CA2,CA5,CA9 = Arc('CA2',C,A,2),Arc('CA5',C,A,5),Arc('CA9',C,A,9)
>>> g.setNoeuds([A,B,C])
>>> g.setArcs([AB2,BC7,BC4,CA2,CA5,CA9])
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB2', 'BC7', 'BC4', 'CA2', 'CA5', 'CA9'])
>>> g.simplification()
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-2->B', 'B-11->C', 'C-16->A'])
>>> # Test sur le graphe du document de cours
>>> g1 = Graphe('graphe1')
>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
>>> # arcs partant de A
>>> AB,ABbis,AC = Arc('AB',A,B,5),Arc('ABbis',A,B,2),Arc('AC',A,C,3)
>>> # arcs arrivant sur A
>>> CA,CAbis = Arc('CA',C,A,8),Arc('CAbis',C,A,1)
>>> # arc entre B et C
>>> BC = Arc('BC',B,C,5)
>>> g1.setNoeuds([A,B,C])
>>> g1.setArcs([AB,ABbis,AC,CA,CAbis,BC])
>>> print(g1)
Graphe(nom=graphe1, noeuds=['A', 'B', 'C'], arcs=['AB', 'ABbis', 'AC', 'CA', 'CAbis', 'BC'])
>>> for noeud in g1.getNoeuds():
... print(noeud)
...
Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0)
Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=0)
Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0)
>>> g1.simplification()
>>> print(g1)
Graphe(nom=graphe1, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A'])
>>> for noeud in g1.getNoeuds():
... print(noeud)
...
Noeud(nom=A, voisins=['B', 'C'], nbArcs=3, capital=0)
Noeud(nom=B, voisins=['A', 'C'], nbArcs=2, capital=0)
Noeud(nom=C, voisins=['A', 'B'], nbArcs=3, capital=0)
""" # noqa : E501
pass
# Si A et B sont voisins alors
# soit A a des dettes envers B, soit B a des dettes envers A.
newArcs = []
for noeud in self.getNoeuds():
for voisin in noeud.getVoisins():
detteTotale = 0
for arc in self.getArcs():
if (noeud == arc.getNoeudDebut() and
voisin == arc.getNoeudFin()):
# On cumule la dette
detteTotale += arc.getPoids()
if detteTotale != 0:
# On ajoute un nouvel arc avec la dette totale
# au tableau des nouveaux arcs
nouveauNom = noeud.getNom()+'-'+str(detteTotale)+'->'+voisin.getNom() # noqa : E501
newArc = Arc(nouveauNom, noeud, voisin, detteTotale)
newArcs.append(newArc)
# Le graphe garde les mêmes noeuds
# Mais on remplace tous les arcs
# On commence par supprimer tous les arcs existants
index = len(self.getArcs())-1
while index != -1:
self.removeArc(self.getArcs()[index])
index -= 1
# On ajoute les nouveaux arcs
self.setArcs(newArcs)
# Il faut aussi redefinir tous les voisins
for arc in newArcs:
if not arc.getNoeudDebut().isVoisin(arc.getNoeudFin()):
arc.getNoeudDebut().addVoisin(arc.getNoeudFin())
# Il faut aussi redefinir le nombre d'arcs de chaque noeud
# On commence par mettre le nombre d'arcs à 0
for noeud in self.getNoeuds():
noeud.setNbArcs(0)
# On met à jour le nombre d'arcs de chaque noeud
for arc in newArcs:
if noeud == arc.getNoeudDebut():
noeud.setNbArcs(noeud.getNbArcs()+1)
if noeud == arc.getNoeudFin():
noeud.setNbArcs(noeud.getNbArcs()+1)
def getArcsDirects(self) -> list:
"""Renvoie la liste des arcs directs du graphe.
Tests
-----
>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
>>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
>>> g = Graphe('graphe')
>>> g.setNoeuds([A,B,C])
>>> g.setArcs([AB5,AB2,AC3])
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C'])
>>> # Pour les tests, on ajoute les arcs inverses à la main
>>> # car la méthode addArcsInversesNuls() simplifie le graphe s'il ne l'est pas
>>> BA5i,BA2i,CA3i = Arc('B-5->A(i)',B,A,0),Arc('B-2->A(i)',B,A,0),Arc('C-3->A(i)',C,A,0)
>>> arcsInverses = [BA5i,BA2i,CA3i]
>>> for arc in arcsInverses:
... arc.setEstInverse(True)
>>> g.addArcs([BA5i,BA2i,CA3i])
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->A(i)', 'B-2->A(i)', 'C-3->A(i)'])
>>> for arc in g.getArcsDirects():
... print(arc.getNom())
...
A-5->B
A-2->B
A-3->C
>>> g.addArcsInversesNuls()
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-0->A', 'C-0->A'])
>>> for arc in g.getArcsDirects():
... print(arc.getNom())
...
A-7->B
A-3->C
""" # noqa : E501
pass
arcsDirects = []
for arc in self.getArcs():
if not arc.getEstInverse():
arcsDirects.append(arc)
return arcsDirects
def getArcsInverses(self) -> list:
"""Renvoie la liste des arcs inverses du graphe.
Tests
-----
>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
>>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
>>> g = Graphe('graphe')
>>> g.setNoeuds([A,B,C])
>>> g.setArcs([AB5,AB2,AC3])
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C'])
>>> # Pour les tests, on ajoute les arcs inverses à la main
>>> # car la méthode addArcsInversesNuls() simplifie le graphe s'il ne l'est pas
>>> BA5i,BA2i,CA3i = Arc('B-5->A(i)',B,A,0,True),Arc('B-2->A(i)',B,A,0,True),Arc('C-3->A(i)',C,A,0,True)
>>> arcsInverses = [BA5i,BA2i,CA3i]
>>> g.addArcs([BA5i,BA2i,CA3i])
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->A(i)', 'B-2->A(i)', 'C-3->A(i)'])
>>> for arc in g.getArcsInverses():
... print(arc.getNom())
B-5->A(i)
B-2->A(i)
C-3->A(i)
>>> g.addArcsInversesNuls()
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-0->A', 'C-0->A'])
>>> for arc in g.getArcsInverses():
... print(arc.getNom())
...
B-0->A
C-0->A
""" # noqa : E501
pass
arcsInverses = []
for arc in self.getArcs():
if arc.getEstInverse():
arcsInverses.append(arc)
return arcsInverses
def estSimplifie(self) -> bool:
"""Retourne un booléen selon que le graphe est simplifié ou non.
Tests
-----
>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
>>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
>>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
>>> g = Graphe('graphe')
>>> g.setNoeuds([A,B,C])
>>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1])
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A'])
>>> g.estSimplifie()
False
>>> g1 = copy.deepcopy(g)
>>> g1.simplification()
>>> print(g1)
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A'])
>>> g1.estSimplifie()
True
>>> g2 = Graphe('graphe2')
>>> g2.setNoeuds([A,B])
>>> AB1,BA1 = Arc('A-1->B(1)',A,B,1),Arc('B-1->A(1)',B,A,1)
>>> g2.setArcs([AB1,BA1])
>>> print(g2)
Graphe(nom=graphe2, noeuds=['A', 'B'], arcs=['A-1->B(1)', 'B-1->A(1)'])
>>> g2.addArcsInversesNuls()
>>> print(g2)
Graphe(nom=graphe2, noeuds=['A', 'B'], arcs=['A-1->B(1)', 'B-1->A(1)', 'B-0->A', 'A-0->B'])
>>> # la méthode estSimplifie() ne doit tenir compte que des arcs directs
>>> g2.estSimplifie()
True
""" # noqa : E501
pass
arcsDirects = self.getArcsDirects()
estSimplifie = True
for i in range(len(arcsDirects)):
k = 0
for j in range(len(arcsDirects)):
if (arcsDirects[i].getNoeudDebut() == arcsDirects[j].getNoeudDebut() and # noqa : E501
arcsDirects[i].getNoeudFin() == arcsDirects[j].getNoeudFin()): # noqa : E501
k += 1
if k > 1:
estSimplifie = False
return estSimplifie
def parcoursEnProfondeurNoeuds(
self,
noeudDepart: Noeud,
dejaVisites=None
) -> list:
"""Renvoie les noeuds du graphe accessibles depuis noeudDepart.
Paramètres
----------
- noeudDepart : Noeud - Le noeud de départ de l'exploration.
- dejaVisites : list - Une liste avec les noeuds déjà visités.
Remarque
--------
- Fonction recursive
Tests
-----
>>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
>>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
>>> BA = Arc('B->A',B,A,1)
>>> CB = Arc('C->B',C,B,1)
>>> DE = Arc('D->E',D,E,2)
>>> EA = Arc('E->A',E,A,3)
>>> g = Graphe('graphe')
>>> g.setNoeuds([A,B,C,D,E])
>>> g.setArcs([AB,AC,AD,CB,DE,EA])
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'A->D', 'C->B', 'D->E', 'E->A'])
>>> for n in g.parcoursEnProfondeurNoeuds(A,[]):
... print(n.getNom())
...
A
B
C
D
E
>>> g1 = Graphe('graphe1')
>>> g1.setNoeuds([A,B,C,D,E])
>>> g1.setArcs([AB,AC,BA,CB,DE,EA])
>>> print(g1)
Graphe(nom=graphe1, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'B->A', 'C->B', 'D->E', 'E->A'])
>>> for n in g1.parcoursEnProfondeurNoeuds(A,[]):
... print(n.getNom())
...
A
B
C
""" # noqa : E501
pass
assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud." # noqa : E501
assert isinstance(dejaVisites,list) or dejaVisites is None,"dejaVisites vaut None ou est une liste." # noqa : E501
if dejaVisites is None:
dejaVisites = []
if noeudDepart not in dejaVisites:
dejaVisites.append(noeudDepart)
for voisin in self.getCreanciers(noeudDepart):
self.parcoursEnProfondeurNoeuds(voisin, dejaVisites)
return dejaVisites
def getArcsCreanciers(self, noeudDepart: Noeud) -> list:
"""Renvoie une liste de tous les arcs partants de noeudDepart.
Paramètres
----------
noeudDepart : Noeud - Le noeud dont on veut la liste des créanciers.
Tests
-----
>>> g = Graphe('graphe')
>>> A,B,D,E = Noeud('A'),Noeud('B'),Noeud('D'),Noeud('E')
>>> AB1,AB2,AB3 = Arc('A->B(1)',A,B,1),Arc('A->B(2)',A,B,1),Arc('A->B(3)',A,B,1)
>>> DE = Arc('D->E',D,E,1)
>>> EA = Arc('E->A',E,A,1)
>>> g.setNoeuds([A,B,D,E])
>>> g.setArcs([AB1,AB2,AB3,DE,EA])
>>> for a in g.getArcsCreanciers(A):
... print(a.getNom())
...
A->B(1)
A->B(2)
A->B(3)
>>> for a in g.getArcsCreanciers(D):
... print(a.getNom())
...
D->E
""" # noqa : E501
pass
assert self.isNoeudInGraphe(noeudDepart), "noeudDepart doit être un noeud du graphe." # noqa : E501
# Ne pas utiliser les assertions pour valider les données
# car elles peuvent être désactivées.
# On lève donc une erreur en cas de souci
if not self.isNoeudInGraphe(noeudDepart):
raise ValueError("noeudDepart doit être un noeud du graphe.")
else:
arcsCreanciers = []
for arc in self.getArcs():
if arc.getNoeudDebut() == noeudDepart:
arcsCreanciers.append(arc)
return arcsCreanciers
def parcoursEnProfondeurArcs(
self,
noeudDepart: Noeud,
dejaVisites=None
) -> list:
"""Renvoie les arcs du graphe accessibles depuis noeudDepart.
Paramètres
----------
- noeudDepart : Noeud - Le noeud dont on veut les arcs accessibles.
- dejaVisites : list - Les arcs déjà visités.
Remarque
--------
- Fonction recursive
Tests
-----
>>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
>>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
>>> BA = Arc('B->A',B,A,1)
>>> CB = Arc('C->B',C,B,1)
>>> DE = Arc('D->E',D,E,2)
>>> EA = Arc('E->A',E,A,3)
>>> g = Graphe('graphe')
>>> g.setNoeuds([A,B,C,D,E])
>>> g.setArcs([AB,AC,AD,CB,DE,EA])
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'A->D', 'C->B', 'D->E', 'E->A'])
>>> for n in g.parcoursEnProfondeurArcs(A,[]):
... print(n.getNom())
...
A->B
C->B
D->E
E->A
A->C
A->D
>>> g1 = Graphe('graphe1')
>>> g1.setNoeuds([A,B,C,D,E])
>>> g1.setArcs([AB,AC,BA,CB,DE,EA])
>>> print(g1)
Graphe(nom=graphe1, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'B->A', 'C->B', 'D->E', 'E->A'])
>>> for n in g1.parcoursEnProfondeurArcs(A,[]):
... print(n.getNom())
...
A->B
B->A
A->C
C->B
""" # noqa : E501
pass
assert isinstance(noeudDepart, Noeud), "arcDepart doit être un instance de la classe Arc." # noqa : E501
assert isinstance(dejaVisites,list) or dejaVisites is None,"dejaVisites vaut None ou est une liste." # noqa : E501
if dejaVisites is None:
dejaVisites = []
for arc in self.getArcsCreanciers(noeudDepart):
if arc not in dejaVisites:
dejaVisites.append(arc)
for voisin in self.getCreanciers(noeudDepart):
self.parcoursEnProfondeurArcs(voisin, dejaVisites)
return dejaVisites
def hasCycleFromNoeud(self, noeudCandidat: Noeud) -> bool:
"""Renvoie un booléen selon que le graphe contient un cycle à partir du noeudCandidat ou non.
Paramètres
----------
noeudCandidat : Noeud - Un noeud du graphe.
Tests
-----
>>> g = Graphe('graphe')
>>> A,B,C,D,E = Noeud('0'),Noeud('1'),Noeud('2'),Noeud('3'),Noeud('4')
>>> AB,AC,AD = Arc('0->1',A,B,1),Arc('0->2',A,C,1),Arc('0->3',A,D,1)
>>> CB = Arc('2->1',C,B,1)
>>> DE = Arc('3->4',D,E,1)
>>> EA = Arc('4->0',E,A,1)
>>> g.setNoeuds([A,B,C,D,E])
>>> g.setArcs([AB,AC,AD,CB,DE,EA])
>>> print(g)
Graphe(nom=graphe, noeuds=['0', '1', '2', '3', '4'], arcs=['0->1', '0->2', '0->3', '2->1', '3->4', '4->0'])
>>> # Ce graphe contient un cycle à partir des noeuds A,D ou E : A->D->E->A
>>> print(g.hasCycleFromNoeud(A))
True
>>> print(g.hasCycleFromNoeud(B))
False
>>> print(g.hasCycleFromNoeud(C))
False
>>> print(g.hasCycleFromNoeud(D))
True
>>> print(g.hasCycleFromNoeud(E))
True
""" # noqa : E501
pass
assert isinstance(noeudCandidat, Noeud), "Le noeudCandidat doit être un instance de la classe Noeud." # noqa : E501
# Il suffit que l'un des creanciers du noeudCandidat
# puisse accéder à nouveau à noeudCandidat
boolDeSortie = False
index = 1
noeudsAccessibles = self.parcoursEnProfondeurNoeuds(noeudCandidat)
for index in range(1, len(noeudsAccessibles)):
if noeudCandidat in self.parcoursEnProfondeurNoeuds(noeudsAccessibles[index]): # noqa : E501
boolDeSortie = True
return boolDeSortie
def hasCycle(self) -> None:
"""Renvoie un booléen selon que le graphe a encore au moins un cycle.
Tests
-----
>>> g = Graphe('graphe')
>>> A,B,C,D,E = Noeud('0'),Noeud('1'),Noeud('2'),Noeud('3'),Noeud('4')
>>> AB,AC,AD = Arc('0->1',A,B,1),Arc('0->2',A,C,1),Arc('0->3',A,D,1)
>>> CB = Arc('2->1',C,B,1)
>>> DE = Arc('3->4',D,E,1)
>>> EA = Arc('4->0',E,A,1)
>>> g.setNoeuds([A,B,C,D,E])
>>> g.setArcs([AB,AC,AD,CB,DE,EA])
>>> print(g)
Graphe(nom=graphe, noeuds=['0', '1', '2', '3', '4'], arcs=['0->1', '0->2', '0->3', '2->1', '3->4', '4->0'])
>>> # Ce graphe contient un cycle à partir des noeuds A,D ou E : A->D->E->A
>>> g.hasCycle()
True
>>> g1 = Graphe('graphe1')
>>> g1.setNoeuds([A,B,C,D,E])
>>> g1.setArcs([AB,AC,AD,CB,EA])
>>> print(g1)
Graphe(nom=graphe1, noeuds=['0', '1', '2', '3', '4'], arcs=['0->1', '0->2', '0->3', '2->1', '4->0'])
>>> # Ce graphe ne contient pas de cycle
>>> g1.hasCycle()
False
""" # noqa : E501
pass
boolDeSortie = False
index = 0
noeudsDuGraphe = self.getNoeuds()
while (not self.hasCycleFromNoeud(noeudsDuGraphe[index]) and
index < len(noeudsDuGraphe)-1):
index += 1
if index < len(noeudsDuGraphe)-1:
boolDeSortie = True
return boolDeSortie
def getNodesWays(
self,
noeudDepart: Noeud,
noeudArrivee: Noeud,
way=None
) -> list:
"""Renvoie une liste de liste de noeuds contenant tous les chemins entre noeudDepart et
noeudArrivee s'il en existe.
Renvoie [] sinon.
Paramètres
----------
- noeudDepart : Noeud - Le noeud de départ du chemin.
- noeudArrivee : Noeud - Le noeud d'arrivée du chemin.
- way : list - Un tableau contenant les noeuds permettant d'aller de noeudDepart à noeudArrivee
Remarques
---------
- Fonction recursive
- Ne trouve pas les cycles
- S'il existe plusieurs arcs entre deux noeuds, le chemin de noeud est identique et
ressort donc plusieurs fois. Ce sera utile pour compter les chemins d'arcs distincts.
Tests
-----
>>> g = Graphe('graphe')
>>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
>>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
>>> CB = Arc('C->B',C,B,1)
>>> DE = Arc('D->E',D,E,1)
>>> EA = Arc('E->A',E,A,1)
>>> g.setNoeuds([A,B,C,D,E])
>>> g.setArcs([AB,AC,AD,CB,DE,EA])
>>> # Aucun chemin de B à C
>>> for chemin in g.getNodesWays(B,C):
... print("====chemin====")
... for n in chemin:
... print(n)
...
>>> # Deux chemins entre A et B
>>> for chemin in g.getNodesWays(A,B):
... print("====chemin====")
... for n in chemin:
... print(n)
...
====chemin====
Noeud(nom=A, voisins=['B', 'C', 'D', 'E'], nbArcs=4, capital=0)
Noeud(nom=B, voisins=['A', 'C'], nbArcs=2, capital=0)
====chemin====
Noeud(nom=A, voisins=['B', 'C', 'D', 'E'], nbArcs=4, capital=0)
Noeud(nom=C, voisins=['A', 'B'], nbArcs=2, capital=0)
Noeud(nom=B, voisins=['A', 'C'], nbArcs=2, capital=0)
>>> g1 = Graphe('graphe1')
>>> A1,B1,C1,D1 = Noeud('A1'),Noeud('B1'),Noeud('C1'),Noeud('D1')
>>> A1B1_1,A1B1_2,A1B1_3 = Arc('A1->B1(1)',A1,B1,1),Arc('A1->B1(2)',A1,B1,1),Arc('A1->B1(3)',A1,B1,1)
>>> B1C1_1,B1C1_2 = Arc('B1->C1(1)',B1,C1,1),Arc('B1->C1(2)',B1,C1,1)
>>> C1A1 = Arc('C1->A1',C1,A1,1)
>>> C1D1 = Arc('C1->D1',C1,D1,1)
>>> g1.setNoeuds([A1,B1,C1,D1])
>>> g1.setArcs([A1B1_1,A1B1_2,A1B1_3,B1C1_1,B1C1_2,C1A1,C1D1])
>>> # Six chemins entre A1 et D1
>>> for chemin in g1.getNodesWays(A1,D1):
... print("====chemin====")
... for n in chemin:
... print(n)
...
====chemin====
Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0)
Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0)
Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0)
Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0)
====chemin====
Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0)
Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0)
Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0)
Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0)
====chemin====
Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0)
Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0)
Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0)
Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0)
====chemin====
Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0)
Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0)
Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0)
Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0)
====chemin====
Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0)
Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0)
Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0)
Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0)
====chemin====
Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0)
Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0)
Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0)
Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0)
""" # noqa : E501
pass
assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud." # noqa : E501
assert isinstance(noeudArrivee, Noeud), "Le noeudArrivee doit être un instance de la classe Noeud." # noqa : E501
assert isinstance(way, list) or way is None, "way vaut None ou est une liste." # noqa : E501
if way is None:
way = []
way = way + [noeudDepart]
if noeudDepart == noeudArrivee:
return [way]
ways = []
nonVisites = []
# Pour l'algorithme de Klein, notamment,
# le fait de rechercher tous les chemins
# fait exploser le temps de traitement sur des graphes de
# taille réelle dès Gen_100
creanciersNoeudDepart = self.getCreanciers(noeudDepart)
for noeud in creanciersNoeudDepart:
if noeud not in way:
nonVisites.append(noeud)
for noeud in nonVisites:
ways.extend(self.getNodesWays(noeud, noeudArrivee, way))
return ways
def getArcsWays(
self,
noeudDepart: Noeud,
noeudArrivee: Noeud,
way=None
) -> list:
"""Renvoie une liste de tuples d'arcs contenant tous les chemins entre noeudDepart et noeudArrivee
s'il en existe.
Renvoie [] sinon.
Paramètres
----------
- noeudDepart : Noeud - Le noeud de départ du chemin.
- noeudArrivee : Noeud - Le noeud d'arrivée du chemin.
- way : list - un tableau contenant les arcs permettant d'aller de noeudDepart à noeudArrivee.
Remarques
---------
- Ne trouve pas les cycles
- S'il existe plusieurs arcs entre deux noeuds, le chemin de noeud est identique et
ressort donc plusieurs fois. Ce sera utile pour compter les chemins d'arcs distincts.
Tests
-----
>>> g = Graphe('grapheLinéaire')
>>> A,B,C,D = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D')
>>> AB1,AB2,AB3 = Arc('A->B(1)',A,B,1),Arc('A->B(2)',A,B,1),Arc('A->B(3)',A,B,1)
>>> BC1,BC2 = Arc('B->C(1)',B,C,1),Arc('B->C(2)',B,C,1)
>>> CD1,CD2,CD3,CD4 = Arc('C->D(1)',C,D,1),Arc('C->D(2)',C,D,1),Arc('C->D(3)',C,D,1),Arc('C->D(4)',C,D,1)
>>> g.setNoeuds([A,B,C,D])
>>> g.setArcs([AB1,AB2,AB3,BC1,BC2,CD1,CD2,CD3,CD4])
>>> # Il y a 24 chemins d'arcs possibles entre A et D
>>> len(g.getArcsWays(A,D))
24
>>> # Il y a 6 chemins d'arcs possibles entre A et C
>>> # On les liste tous
>>> len(g.getArcsWays(A,C))
6
>>> for arcsWay in g.getArcsWays(A,C):
... print("=== Chemin d'arcs entre A et C ===")
... for arc in arcsWay:
... print(arc.getNom())
...
=== Chemin d'arcs entre A et C ===
A->B(1)
B->C(1)
=== Chemin d'arcs entre A et C ===
A->B(1)
B->C(2)
=== Chemin d'arcs entre A et C ===
A->B(2)
B->C(1)
=== Chemin d'arcs entre A et C ===
A->B(2)
B->C(2)
=== Chemin d'arcs entre A et C ===
A->B(3)
B->C(1)
=== Chemin d'arcs entre A et C ===
A->B(3)
B->C(2)
>>> # Tests sur un graphe non linéaire
>>> # On ajoute deux arcs entre C et A
>>> CA1,CA2 = Arc('C->A(1)',C,A,1),Arc('C->A(2)',C,A,1)
>>> # On ajoute un arc entre A et C
>>> AC1 = Arc('A->C(1)',A,C,1)
>>> # Le graphe aura donc un cycle ABCA
>>> # La méthode ne tolèrant pas de repasser par le noeud de départ
>>> # Elle trouve bien 7 chemins entre A et C
>>> g1 = Graphe('grapheNonLinéaire')
>>> g1.setNoeuds([A,B,C,D])
>>> g1.setArcs([AB1,AB2,AB3,BC1,BC2,CD1,CD2,CD3,CD4,CA1,CA2,AC1])
>>> len(g1.getArcsWays(A,C))
7
>>> for arcsWay in g1.getArcsWays(A,C):
... print("=== Chemin d'arcs entre A et C ===")
... for arc in arcsWay:
... print(arc.getNom())
...
=== Chemin d'arcs entre A et C ===
A->B(1)
B->C(1)
=== Chemin d'arcs entre A et C ===
A->B(1)
B->C(2)
=== Chemin d'arcs entre A et C ===
A->B(2)
B->C(1)
=== Chemin d'arcs entre A et C ===
A->B(2)
B->C(2)
=== Chemin d'arcs entre A et C ===
A->B(3)
B->C(1)
=== Chemin d'arcs entre A et C ===
A->B(3)
B->C(2)
=== Chemin d'arcs entre A et C ===
A->C(1)
""" # noqa : E501
pass
assert isinstance(noeudDepart, Noeud), "noeudDepart doit être un instance de la classe Noeud." # noqa : E501
assert isinstance(noeudArrivee, Noeud), "Le noeudArrivee doit être un instance de la classe Noeud." # noqa : E501
assert isinstance(way, list) or way is None, "way vaut None ou est une liste." # noqa : E501
assert noeudDepart != noeudArrivee, "noeudDepart et nouedArrivee doivent être distincts" # noqa : E501
# Pour stocker tous les chemins d'arcs possibles
arcsWays = []
# On liste tous les chemins de noeuds
cheminsDeNoeudsPossibles = self.getNodesWays(noeudDepart, noeudArrivee)
cheminsDeNoeudsSansDoublons = []
for chemin in cheminsDeNoeudsPossibles:
if chemin not in cheminsDeNoeudsSansDoublons:
cheminsDeNoeudsSansDoublons.append(chemin)
# Pour chaque chemin de cheminsDeNoeudsSansDoublon
# On va récupérer tous les arcs entre deux noeuds successifs
# dans arcsWay
for chemin in cheminsDeNoeudsSansDoublons:
# le nombre d'espace inter Noeuds
nbInterNoeuds = len(chemin)-1
# Pour stocker tous les arcs possibles entre deux noeuds du chemin
arcsWay = []
for i in range(nbInterNoeuds):
arcsWay.append(self.getArcsBetween(chemin[i], chemin[i + 1]))
# On va stocker toutes les combinaisons d'arcs de ce chemin
produitCartesienDesArcs = []
for combs in product(*arcsWay):
produitCartesienDesArcs.append(combs)
arcsWays.extend(produitCartesienDesArcs)
return arcsWays
def getAllNodesCyclesFromNoeud(self, noeudDepart: Noeud) -> list:
"""Renvoie tous les cycles partant du noeudDepart s'il en existe.
Paramètres
----------
noeudDepart : Noeud - Le noeud de départ des cycles.
Tests
-----
>>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
>>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
>>> BA = Arc('B->A',B,A,1)
>>> CB = Arc('C->B',C,B,1)
>>> DE = Arc('D->E',D,E,2)
>>> EA = Arc('E->A',E,A,3)
>>> g = Graphe('graphe')
>>> g.setNoeuds([A,B,C,D,E])
>>> g.setArcs([AB,AC,AD,CB,DE,EA])
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'A->D', 'C->B', 'D->E', 'E->A'])
>>> for cycle in g.getAllNodesCyclesFromNoeud(A):
... print("====cycle====")
... for n in cycle:
... print(n.getNom())
...
====cycle====
A
D
E
A
>>> g1 = Graphe('graphe1')
>>> g1.setNoeuds([A,B,C,D,E])
>>> g1.setArcs([AB,AC,BA,CB,DE,EA])
>>> print(g1)
Graphe(nom=graphe1, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'B->A', 'C->B', 'D->E', 'E->A'])
>>> for cycle in g1.getAllNodesCyclesFromNoeud(A):
... print("====cycle====")
... for n in cycle:
... print(n.getNom())
...
====cycle====
A
B
A
====cycle====
A
C
B
A
>>> A1,B1,C1 = Noeud('A'),Noeud('B'),Noeud('C')
>>> A1B1,A1B1bis,A1C1 = Arc('A-5->B',A1,B1,5),Arc('A-2->B',A1,B1,2),Arc('A-3->C',A1,C1,3)
>>> B1C1,C1A1,C1A1bis = Arc('B-5->C',B1,C1,5),Arc('C-8->A',C1,A1,8),Arc('C-1->A',C1,A1,1)
>>> g2 = Graphe('graphe2')
>>> g2.setNoeuds([A1,B1,C1])
>>> g2.setArcs([A1B1,A1B1bis,A1C1,B1C1,C1A1,C1A1bis])
>>> print(g2)
Graphe(nom=graphe2, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A'])
>>> # Il y a 6 cycles distincts partant de A1
>>> # On les affiche tous, ils semblent en doublon mais ce sont les arcs les liant qui diffèrent.
>>> for cycle in g2.getAllNodesCyclesFromNoeud(A1):
... print("====cycle====")
... for n in cycle:
... print(n.getNom())
...
====cycle====
A
B
C
A
====cycle====
A
B
C
A
====cycle====
A
B
C
A
====cycle====
A
B
C
A
====cycle====
A
C
A
====cycle====
A
C
A
""" # noqa : E501
pass
assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud." # noqa : E501
allCycles = []
if self.hasCycleFromNoeud(noeudDepart):
for noeud in self.getCreanciers(noeudDepart):
for i in range(len(self.getNodesWays(noeud, noeudDepart))):
cycle = []
cycle = [noeudDepart]
cycle.extend(self.getNodesWays(noeud, noeudDepart)[i])
allCycles.append(cycle)
return allCycles
def getArcsBetween(self, noeudDepart: Noeud, noeudArrivee: Noeud) -> list:
"""Renvoie tous les arcs entre noeudDepart et noeudArrivee.
Paramètres
----------
- noeudDepart : Noeud - Le noeud de départ des arcs.
- noeudArrivee : Noeud - Le noeud de d'arrivée des arcs.
Tests
-----
>>> g = Graphe('graphe')
>>> A,B,D,E = Noeud('A'),Noeud('B'),Noeud('D'),Noeud('E')
>>> AB1,AB2,AB3 = Arc('A->B(1)',A,B,1),Arc('A->B(2)',A,B,1),Arc('A->B(3)',A,B,1)
>>> DE = Arc('D->E',D,E,1)
>>> EA = Arc('E->A',E,A,1)
>>> g.setNoeuds([A,B,D,E])
>>> g.setArcs([AB1,AB2,AB3,DE,EA])
>>> for a in g.getArcsBetween(A,B):
... print(a.getNom())
...
A->B(1)
A->B(2)
A->B(3)
>>> # Aucun arc de D à A
>>> for a in g.getArcsBetween(D,A):
... print(a.getNom())
...
""" # noqa : E501
pass
assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud." # noqa : E501
assert isinstance(noeudArrivee, Noeud), "Le noeudArrivee doit être un instance de la classe Noeud." # noqa : E501
arcs = []
for arc in self.getArcs():
if (arc.getNoeudDebut() == noeudDepart and
arc.getNoeudFin() == noeudArrivee):
arcs.append(arc)
return arcs
def getArcsWay(self, noeudDepart: Noeud, noeudArrivee: Noeud) -> list:
"""Renvoie une liste d'arcs contenant un chemin entre noeudDepart et noeudArrivee s'il en existe.
Renvoie [] sinon.
Paramètres
----------
- noeudDepart : Noeud - Le noeud de départ des arcs du chemin.
- noeudArrivee : Noeud - Le noeud de d'arrivée des arcs du chemin.
Remarque
--------
Ne trouve pas les cycles
Choisit un arc au hasard s'il existe plusieurs arcs parallèles.
noeudDepart : Noeud - noeud de départ
noeudArrivee : Noeud - noeud d'arrivée
Tests
-----
>>> g = Graphe('graphe')
>>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
>>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
>>> CB = Arc('C->B',C,B,1)
>>> CBbis = Arc('C-2->B',C,B,2)
>>> DE = Arc('D->E',D,E,1)
>>> EA = Arc('E->A',E,A,1)
>>> g.setNoeuds([A,B,C,D,E])
>>> g.setArcs([AB,AC,AD,CB,CBbis,DE,EA])
>>> # Aucun chemin de B à C
>>> for arc in g.getArcsWay(B,C):
... print(arc)
...
>>> # Dans ce graphe trois chemins sont possibles entre A et B
>>> # Deux des trois chemins présentent des arcs parallèles.
>>> # On fixe la graine pour les tests
>>> random.seed(13)
>>> for arc in g.getArcsWay(A,B):
... print(arc.getNom())
...
A->C
C->B
>>> random.seed(27)
>>> for arc in g.getArcsWay(A,B):
... print(arc.getNom())
...
A->C
C-2->B
>>> random.seed(3)
>>> for arc in g.getArcsWay(A,B):
... print(arc.getNom())
...
A->B
""" # noqa : E501
pass
assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud." # noqa : E501
assert isinstance(noeudArrivee, Noeud), "Le noeudArrivee doit être un instance de la classe Noeud." # noqa : E501
if self.getNodesWays(noeudDepart, noeudArrivee):
arcsWay = random.choice(
self.getArcsWays(noeudDepart, noeudArrivee)
)
else:
arcsWay = []
return arcsWay
def getArcsCycleFromNoeud(self, noeudDepart: Noeud) -> list:
"""Renvoie une liste d'arcs représentant un cycle partant du noeudDepart s'il en existe.
[] sinon.
Paramètres
----------
- noeudDepart : Noeud - Le noeud de départ du cycle.
Remarque
--------
S'il existe plusieurs cycles à partir d'un noeud, l'algorithme n'en renvoie qu'un seul au hasard.
Tests
-----
>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
>>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
>>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
>>> g2 = Graphe('graphe2')
>>> g2.setNoeuds([A,B,C])
>>> g2.setArcs([AB5,AB2,AC3,BC5,CA8,CA1])
>>> print(g2)
Graphe(nom=graphe2, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A'])
>>> # Il y a 6 cycles partants de A dans ce graphe
>>> # On les récupère tous en fixant la graine
>>> random.seed(1)
>>> for arc in g2.getArcsCycleFromNoeud(A):
... print(arc.getNom())
...
A-5->B
B-5->C
C-1->A
>>> random.seed(2)
>>> for arc in g2.getArcsCycleFromNoeud(A):
... print(arc.getNom())
...
A-5->B
B-5->C
C-8->A
>>> random.seed(6)
>>> for arc in g2.getArcsCycleFromNoeud(A):
... print(arc.getNom())
...
A-2->B
B-5->C
C-1->A
>>> random.seed(4)
>>> for arc in g2.getArcsCycleFromNoeud(A):
... print(arc.getNom())
...
A-2->B
B-5->C
C-8->A
>>> random.seed(5)
>>> for arc in g2.getArcsCycleFromNoeud(A):
... print(arc.getNom())
...
A-3->C
C-1->A
>>> random.seed(24)
>>> for arc in g2.getArcsCycleFromNoeud(A):
... print(arc.getNom())
...
A-3->C
C-8->A
""" # noqa : E501
pass
assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud." # noqa : E501
arcsCycle = []
if self.hasCycleFromNoeud(noeudDepart):
noeudsPossibles = []
for noeud in self.getCreanciers(noeudDepart):
if (self.getNodesWays(noeud, noeudDepart) and
noeud not in noeudsPossibles):
noeudsPossibles.append(noeud)
# On choisit aléatoirement un premier noeud parmi ceux
# qui permettent de revenir à noeudDepart
noeudAleatoire = random.choice(noeudsPossibles)
# On ajoute le début du chemin
arcsCycle.extend(self.getArcsWay(noeudDepart, noeudAleatoire))
# On ajoute le reste du chemin
arcsCycle.extend(self.getArcsWay(noeudAleatoire, noeudDepart))
return arcsCycle
def getMinPoidsFromCycle(self, cycle: list) -> int:
"""Renvoie le plus petits poids des arcs contenus dans cycle.
Paramètres
----------
cycle : list - Une liste d'arcs du graphe.
Remarque
--------
Ici on gère les éventuels cycles parallèles
Tests
-----
>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
>>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
>>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
>>> g = Graphe('graphe')
>>> g.setNoeuds([A,B,C])
>>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1])
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A'])
>>> random.seed(None)
>>> random.seed(9)
>>> cycle = g.getArcsCycleFromNoeud(A)
>>> for e in cycle:
... print(e.getNom())
...
A-3->C
C-1->A
>>> g.getMinPoidsFromCycle(cycle)
1
>>> # Sans simplifier le graphe il peut y avoir des cycles parallèles en partant d'un noeud
>>> random.seed(15)
>>> cycle = g.getArcsCycleFromNoeud(A)
>>> for e in cycle:
... print(e.getNom())
...
A-5->B
B-5->C
C-8->A
>>> g.getMinPoidsFromCycle(cycle)
5
>>> random.seed(13)
>>> cycle = g.getArcsCycleFromNoeud(A)
>>> for e in cycle:
... print(e.getNom())
...
A-2->B
B-5->C
C-8->A
>>> g.getMinPoidsFromCycle(cycle)
2
>>> random.seed(17)
>>> cycle = g.getArcsCycleFromNoeud(A)
>>> for e in cycle:
... print(e.getNom())
...
A-2->B
B-5->C
C-1->A
>>> g.getMinPoidsFromCycle(cycle)
1
>>> # Un test avec simplification préalable, il n'y a donc plus de cycles parallèles en partant d'un noeud.
>>> # Mais il peut rester plusieurs cycles partant de A !
>>> g1 = Graphe('graphe1')
>>> g1.setNoeuds([A,B,C])
>>> g1.setArcs([AB5,AB2,AC3,BC5,CA8,CA1])
>>> print(g1)
Graphe(nom=graphe1, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A'])
>>> g1.simplification()
>>> print(g1)
Graphe(nom=graphe1, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A'])
>>> cycle = g1.getArcsCycleFromNoeud(A)
>>> for e in cycle:
... print(e.getNom())
...
A-7->B
B-5->C
C-9->A
>>> g1.getMinPoidsFromCycle(cycle)
5
""" # noqa : E501
pass
# les éléments de cycle doivent être des instances de la classe Arc
# Ne pas utiliser les assertions pour valider les données
# car elles peuvent être désactivées.
# On lève donc une erreur en cas de souci
# Un booléen pour tester si tout est OK
allOk = True
for arc in cycle:
assert (isinstance(arc, Arc) for arc in cycle), "Les éléments de cycle doivent être des instances de la classe Arc" # noqa : E501
if not isinstance(arc, Arc):
allOk = False
raise ValueError(f'Les éléments de cycle doivent être des instances de la classe Arc. Vérifier : {arc.getNom()}') # noqa : E501
assert len(cycle) >= 1, "Le cycle doit avoir au moins un élément."
if len(cycle) < 1:
allOk = False
raise ValueError('Le cycle doit avoir au moins un élément.')
if allOk:
poidsMin = cycle[0].getPoids()
i = 1
while i < len(cycle):
if cycle[i].getPoids() <= poidsMin:
poidsMin = cycle[i].getPoids()
i += 1
return poidsMin
def eliminerCycleFromNoeud(self, noeudDepart: Noeud) -> None:
"""Renvoie l'instance du graphe modifiée.
Si le graphe contient un cycle à partir de noeudDepart, les arcs de ce cycle sont réduits
du plus petit poids des arcs.
Sinon le graphe n'est pas modifié.
Paramètres
----------
noeudDepart : Noeud - Le noeud de départ du cycle à éliminer.
Remarque
--------
Lorsque le graphe présente un cycle à partir de noeudDepart, cela revient à le supprimer.
Tests
-----
>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
>>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
>>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
>>> g = Graphe('graphe')
>>> g.setNoeuds([A,B,C])
>>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1])
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A'])
>>> g.simplification()
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A'])
>>> # Ce graphe contient 2 cycles à partir de A
>>> for cycle in g.getAllNodesCyclesFromNoeud(A):
... print("====cycle====")
... for noeud in cycle:
... print(noeud.getNom())
...
====cycle====
A
B
C
A
====cycle====
A
C
A
>>> # On élimine le premier, il reste le second
>>> random.seed(7)
>>> g.eliminerCycleFromNoeud(A)
>>> for cycle in g.getAllNodesCyclesFromNoeud(A):
... print("====cycle====")
... for noeud in cycle:
... print(noeud.getNom())
...
====cycle====
A
C
A
>>> # On élimine le second
>>> g.eliminerCycleFromNoeud(A)
>>> # Il n'y a plus de cycle partant de A
>>> for cycle in g.getAllNodesCyclesFromNoeud(A):
... print("====cycle====")
... for noeud in cycle:
... print(noeud.getNom())
...
""" # noqa : E501
pass
assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud." # noqa : E501
if self.hasCycleFromNoeud(noeudDepart):
cycleToReduce = self.getArcsCycleFromNoeud(noeudDepart)
minPoids = self.getMinPoidsFromCycle(cycleToReduce)
for arc in cycleToReduce:
arc.setPoids(arc.getPoids()-minPoids)
# On renomme l'arc
nouveauNom = arc.getNoeudDebut().getNom()+'-'+str(arc.getPoids())+'->'+arc.getNoeudFin().getNom() # noqa : E501
arc.setNom(nouveauNom)
if arc.getPoids() == 0:
self.removeArc(arc)
def hasArcInverse(self, arcCandidat: Arc) -> bool:
"""Renvoie un booléen selon que le graphe contient un arc inverse de
arcCandidat ou pas.
Paramètres
----------
arcCandidat : Arc - L'arc dont on veut savoir si il a un arc inverse.
Remarques
---------
- L'arc inverse n'est pas forcément un arc inverse dans le sens que
l'arc d'un cycle de Klein pour être un arc inverse introduit au
debut de l'algorithme.
- On suppose que le graphe est simplifié au préalable afin qu'il n'y
ait qu'un seul arc entre deux noeuds dans un sens donné.
Tests
-----
>>> g = Graphe('graphe')
>>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
>>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
>>> BA = Arc('B->A',B,A,1)
>>> CB = Arc('C->B',C,B,1)
>>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1)
>>> EA = Arc('E->A',E,A,1)
>>> g.setNoeuds([A,B,C,D,E])
>>> g.setArcs([AB,AC,AD,BA,CB,DE,DB,EA])
>>> g.hasArcInverse(AB)
False
>>> # On ajoute tous les arcs inverses au graphe
>>> g.addArcsInversesNuls()
>>> g.hasArcInverse(AB)
True
"""
pass
assert isinstance(arcCandidat, Arc), "arcCandidat doit être un instance de la classe Arc." # noqa : E501
boolDeSortie = False
if not arcCandidat.getEstInverse():
for arc in self.getArcsInverses():
if (arcCandidat.getNoeudDebut() == arc.getNoeudFin() and
arcCandidat.getNoeudFin() == arc.getNoeudDebut()):
boolDeSortie = True
if arcCandidat.getEstInverse():
for arc in self.getArcsDirects():
if (arcCandidat.getNoeudDebut() == arc.getNoeudFin() and
arcCandidat.getNoeudFin() == arc.getNoeudDebut()):
boolDeSortie = True
return boolDeSortie
def getArcInverse(self, arcCandidat: Arc) -> Arc:
"""Renvoie l'arc inverse d'arcCandidat s'il en a un, plante sinon.
Paramètres
----------
arcCandidat : Arc - L'arc dont on veut récupérer l'arc inverse.
Remarques
---------
- L'arc inverse n'est pas forcément un arc inverse dans le sens que
l'arc d'un cycle de Klein peut être un arc inverse introduit au
debut de l'algorithme.
- On suppose que le graphe est simplifié au préalable afin qu'il n'y
ait qu'un seul arc entre deux noeuds dans un sens donné.
Tests
-----
>>> g = Graphe('graphe')
>>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
>>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
>>> BA = Arc('B->A',B,A,1)
>>> CB = Arc('C->B',C,B,1)
>>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1)
>>> EA = Arc('E->A',E,A,1)
>>> g.setNoeuds([A,B,C,D,E])
>>> g.setArcs([AB,AC,AD,BA,CB,DE,DB,EA])
>>> # On ajoute tous les arcs inverses au graphe
>>> g.addArcsInversesNuls()
>>> print(AB.getNom())
A->B
>>> print(g.getArcInverse(AB).getNom())
B-0->A
"""
pass
assert isinstance(arcCandidat, Arc), "arcCandidat doit être un instance de la classe Arc." # noqa : E501
assert self.hasArcInverse(arcCandidat), "arcCandidat doit avoir un arc inverse." # noqa : E501
arcInverse = None
if self.hasArcInverse(arcCandidat):
if not arcCandidat.getEstInverse():
for arc in self.getArcsInverses():
if (arc.getNoeudFin() == arcCandidat.getNoeudDebut() and
arc.getNoeudDebut() == arcCandidat.getNoeudFin()):
arcInverse = arc
if arcCandidat.getEstInverse():
for arc in self.getArcsDirects():
if (arc.getNoeudFin() == arcCandidat.getNoeudDebut() and
arc.getNoeudDebut() == arcCandidat.getNoeudFin()):
arcInverse = arc
return arcInverse
def addArcsInversesNuls(self) -> None:
"""Ajoute un arc inverse de valeur 0 pour chaque arc du graphe.
Remarque
--------
Si le graphe n'est pas simplifié, on le simplifie d'abord.
Tests
-----
>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
>>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
>>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
>>> g = Graphe('graphe')
>>> g.setNoeuds([A,B,C])
>>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1])
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A'])
>>> for arc in g.getArcs():
... print(arc)
...
Arc(nom=A-5->B, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), noeudFin=Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=0), poids=5, estInverse=False)
Arc(nom=A-2->B, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), noeudFin=Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=0), poids=2, estInverse=False)
Arc(nom=A-3->C, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), noeudFin=Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0), poids=3, estInverse=False)
Arc(nom=B-5->C, noeudDebut=Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=0), noeudFin=Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0), poids=5, estInverse=False)
Arc(nom=C-8->A, noeudDebut=Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0), noeudFin=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), poids=8, estInverse=False)
Arc(nom=C-1->A, noeudDebut=Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0), noeudFin=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), poids=1, estInverse=False)
>>> g.addArcsInversesNuls()
>>> for arc in g.getArcs():
... print(arc)
...
Arc(nom=A-7->B, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), noeudFin=Noeud(nom=B, voisins=['A', 'C'], nbArcs=4, capital=0), poids=7, estInverse=False)
Arc(nom=A-3->C, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), noeudFin=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), poids=3, estInverse=False)
Arc(nom=B-5->C, noeudDebut=Noeud(nom=B, voisins=['A', 'C'], nbArcs=4, capital=0), noeudFin=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), poids=5, estInverse=False)
Arc(nom=C-9->A, noeudDebut=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), noeudFin=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), poids=9, estInverse=False)
Arc(nom=B-0->A, noeudDebut=Noeud(nom=B, voisins=['A', 'C'], nbArcs=4, capital=0), noeudFin=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), poids=0, estInverse=True)
Arc(nom=C-0->A, noeudDebut=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), noeudFin=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), poids=0, estInverse=True)
Arc(nom=C-0->B, noeudDebut=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), noeudFin=Noeud(nom=B, voisins=['A', 'C'], nbArcs=4, capital=0), poids=0, estInverse=True)
Arc(nom=A-0->C, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), noeudFin=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), poids=0, estInverse=True)
""" # noqa : E501
pass
assert self.hasArc(), "Le graphe doit avoir des arcs."
if not self.estSimplifie():
self.simplification()
newArcs = []
for arc in self.getArcs():
newName = "{}-0->{}".format(
arc.getNoeudFin().getNom(),
arc.getNoeudDebut().getNom()
)
newArcInverseNul = Arc(
newName,
arc.getNoeudFin(),
arc.getNoeudDebut(),
0,
True
)
newArcs.append(newArcInverseNul)
for arc in newArcs:
self.addArc(arc)
# ================================================================
# =========== METHODES SPECIFIQUES ALGO KLEIN ====================
# ================================================================
def kleinHasCycleFromNoeud(self, noeudCandidat: Noeud) -> bool:
"""Renvoie un booléen selon que le graphe présente un cycle de Klein
ou non à partir du noeudCandidat.
Paramètres
----------
noeudCandidat : Noeud - Une instance de la classe Noeud.
Remarque
--------
Un cycle de Klein est un cycle dont tous les arcs ont un poids non nul.
Tests
-----
>>> g = Graphe('graphe')
>>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
>>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
>>> BA = Arc('B->A',B,A,1)
>>> CB = Arc('C->B',C,B,1)
>>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1)
>>> EA = Arc('E->A',E,A,1)
>>> g.setNoeuds([A,B,C,D,E])
>>> g.setArcs([AB,AC,AD,BA,CB,DE,DB,EA])
>>> g.kleinHasCycleFromNoeud(A)
True
>>> AB.setPoids(0)
>>> g.kleinHasCycleFromNoeud(A)
True
>>> BA.setPoids(0)
>>> g.kleinHasCycleFromNoeud(A)
True
>>> DE.setPoids(0)
>>> g.kleinHasCycleFromNoeud(A)
False
"""
pass
assert isinstance(noeudCandidat, Noeud), "Le noeudCandidat doit être un instance de la classe Noeud." # noqa : E501
# On récupère tous les noeuds situés à un arc de noeudCancidat
noeudDirectementAccessibles = self.getCreanciers(noeudCandidat)
boolDeSortie = False
for noeud in noeudDirectementAccessibles:
if self.kleinArcsBetween(noeudCandidat, noeud):
if self.kleinAllArcsWays(noeud, noeudCandidat):
boolDeSortie = True
return boolDeSortie
def kleinHasCycle(self) -> None:
"""Renvoie un booléen selon que le graphe a encore au moins un cycle de Klein.
Remarque
--------
Un cycle de Klein est un cycle dont tous les arcs ont un poids non nul.
Tests
-----
>>> g = Graphe('graphe')
>>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
>>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
>>> BA = Arc('B->A',B,A,1)
>>> CB = Arc('C->B',C,B,1)
>>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1)
>>> EA = Arc('E->A',E,A,1)
>>> g.setNoeuds([A,B,C,D,E])
>>> g.setArcs([AB,AC,AD,BA,CB,DE,DB,EA])
>>> g.kleinHasCycle()
True
>>> AB.setPoids(0)
>>> g.kleinHasCycle()
True
>>> BA.setPoids(0)
>>> g.kleinHasCycle()
True
>>> DE.setPoids(0)
>>> g.kleinHasCycle()
False
""" # noqa : E501
pass
boolDeSortie = False
index = 0
noeudsDuGraphe = self.getNoeuds()
while (not self.kleinHasCycleFromNoeud(noeudsDuGraphe[index]) and
index < len(noeudsDuGraphe)-1):
index += 1
if index < len(noeudsDuGraphe)-1:
boolDeSortie = True
return boolDeSortie
def kleinArcsBetween(
self,
noeudDepart: Noeud,
noeudArrivee: Noeud
) -> list:
"""Renvoie tous les arcs de poids non nul entre noeudDepart et noeudArrivee.
Paramètres
----------
- noeudDepart : Noeud - Le noeud de départ des arcs.
- noeudArrivee : Noeud - Le noeud de d'arrivée des arcs.
Remarque
--------
Méthode utile pour l'algorithme de Klein
Tests
-----
>>> g = Graphe('graphe')
>>> A,B,D,E = Noeud('A'),Noeud('B'),Noeud('D'),Noeud('E')
>>> AB1,AB2,AB3 = Arc('A->B(1)',A,B,0),Arc('A->B(2)',A,B,1),Arc('A->B(3)',A,B,1)
>>> DEg = Arc('D->E',D,E,1)
>>> EAg = Arc('E->A',E,A,1)
>>> g.setNoeuds([A,B,D,E])
>>> g.setArcs([AB1,AB2,AB3,DEg,EAg])
>>> for a in g.kleinArcsBetween(A,B):
... print(a.getNom())
...
A->B(2)
A->B(3)
>>> # Aucun arc de D à A
>>> for a in g.kleinArcsBetween(D,A):
... print(a.getNom())
...
>>> g1 = Graphe('graphe1')
>>> A1,B1,C1,D1,E1 = Noeud('A1'),Noeud('B1'),Noeud('C1'),Noeud('D1'),Noeud('E1')
>>> AB,AC,AD = Arc('A1->B1',A1,B1,1),Arc('A1->C1',A1,C1,1),Arc('A1->D1',A1,D1,1)
>>> ABbis = Arc('A1->B1(2)',A1,B1,2)
>>> CB = Arc('C1->B1',C1,B1,1)
>>> DE,DB = Arc('D1->E1',D1,E1,1),Arc('D1->B1',D1,B1,1)
>>> EA = Arc('E1->A1',E1,A1,1)
>>> EB = Arc('E1->B1',E1,B1,1)
>>> g1.setNoeuds([A1,B1,C1,D1,E1])
>>> g1.setArcs([AB,AC,AD,ABbis,CB,DE,DB,EA,EB])
>>> # print(g1.kleinArcsBetween(A1,B1))
>>> for a in g1.kleinArcsBetween(A1,B1):
... print(a.getNom())
...
A1->B1
A1->B1(2)
""" # noqa : E501
pass
assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud." # noqa : E501
assert isinstance(noeudArrivee, Noeud), "Le noeudArrivee doit être un instance de la classe Noeud." # noqa : E501
arcs = []
for arc in self.getArcs():
if (arc.getNoeudDebut() == noeudDepart and
arc.getNoeudFin() == noeudArrivee and
arc.getPoids() != 0):
arcs.append(arc)
return arcs
def kleinAllArcsWays(
self,
noeudDepart: Noeud,
noeudArrivee: Noeud
) -> list:
"""Renvoie une liste de tuples :
- d'arcs de poids non nul
- et/ou d'arcs inverses de poids non nul
- ne passant pas deux fois par le même noeud
contenant un chemin entre noeudDepart et noeudArrivee s'il en existe.
Renvoie [] sinon.
Paramètres
----------
- noeudDepart : Noeud - Le noeud de départ des arcs des chemins.
- noeudArrivee : Noeud - Le noeud de d'arrivée des arcs des chemins.
Remarques
---------
- Ne trouve pas les cycles.
- Méthode utile pour l'algorithme de Klein.
- On ne peut pas supprimer de chemins ici en amont de la recherche de cycles
noeudDepart : Noeud - noeud de départ
noeudArrivee : Noeud - noeud d'arrivée
Tests
-----
>>> g = Graphe('graphe')
>>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
>>> AB1,AC,AD = Arc('A->B(1)',A,B,0),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
>>> ABi = Arc('A->B(i)',A,B,1,True)
>>> BA1 = Arc('B->A(1)',B,A,0)
>>> BAi = Arc('B->A(i)',B,A,2,True)
>>> CB = Arc('C->B',C,B,1)
>>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1)
>>> EA,EB = Arc('E->A',E,A,1),Arc('E->B',E,B,1)
>>> g.setNoeuds([A,B,C,D,E])
>>> g.setArcs([AB1,ABi,AC,AD,BA1,BAi,CB,DE,DB,EA,EB])
>>> g.estSimplifie()
True
>>> # Deux chemins de Klein de B à C
>>> # Mais on ne veut que les chemins :
>>> # --> qui ont des arcs directs de poids non nul
>>> # --> et/ou des arcs inverses de poids non nul
>>> # Cela n'en laisse qu'un
>>> for chemin in g.kleinAllArcsWays(B,C):
... print("=== chemin de Klein ===")
... for arc in chemin:
... print(arc.getNom())
...
=== chemin de Klein ===
B->A(i)
A->C
>>> # Dans ce graphe cinq chemins sont possibles entre A et B
>>> # Mais on ne veut que les chemins :
>>> # --> qui ont des arcs directs de poids non nul
>>> # --> et/ou des arcs inverses de poids non nul
>>> # Cela n'en laisse que quatre
>>> for chemin in g.kleinAllArcsWays(A,B):
... print("=== chemin de Klein ===")
... for arc in chemin:
... print(arc.getNom())
=== chemin de Klein ===
A->B(i)
=== chemin de Klein ===
A->C
C->B
=== chemin de Klein ===
A->D
D->E
E->B
=== chemin de Klein ===
A->D
D->B
>>> # On remet un poids non nul sur les arcs AB1 pour retrouver les cinq chemins
>>> AB1.setPoids(1)
>>> for chemin in g.kleinAllArcsWays(A,B):
... print("=== chemin de Klein ===")
... for arc in chemin:
... print(arc.getNom())
=== chemin de Klein ===
A->B(1)
=== chemin de Klein ===
A->B(i)
=== chemin de Klein ===
A->C
C->B
=== chemin de Klein ===
A->D
D->E
E->B
=== chemin de Klein ===
A->D
D->B
>>> # Si on mets un poids nul ailleurs que sur le premier arc d'un chemin
>>> # Le chemin doit être supprimé des possibles aussi
>>> # Cela laisse quatre chemins valides
>>> EB.setPoids(0)
>>> for chemin in g.kleinAllArcsWays(A,B):
... print("=== chemin de Klein ===")
... for arc in chemin:
... print(arc.getNom())
=== chemin de Klein ===
A->B(1)
=== chemin de Klein ===
A->B(i)
=== chemin de Klein ===
A->C
C->B
=== chemin de Klein ===
A->D
D->B
""" # noqa : E501
pass
assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud." # noqa : E501
assert isinstance(noeudArrivee, Noeud), "Le noeudArrivee doit être un instance de la classe Noeud." # noqa : E501
assert noeudDepart != noeudArrivee, "noeudDepart et noeudArrivee doivent être distincts." # noqa : E501
assert self.estSimplifie(), "Les arcs directs du graphe doivent être simplifiés, fusionnés s'ils sont parallèles." # noqa : E501
# Si il existe au moins un chemin entre noeudDepart et noeudArrivee
# On ne garde que les chemins :
# -> dont les arcs ont des poids non nuls
# -> dont les arcs inverses ont des poids non nuls
# -> ne passant pas deux fois par le même noeud
arcsWays = self.getArcsWays(noeudDepart, noeudArrivee)
if arcsWays:
# les chemins à garder, a priori vide
arcsWaysOk = []
for arcsWay in arcsWays:
wayToKeep = True
noeudsConnus = [noeudDepart]
for arc in arcsWay:
if (arc.getPoids() == 0 or
arc.getNoeudFin() in noeudsConnus):
wayToKeep = False
if wayToKeep:
noeudsConnus.append(arc.getNoeudFin())
noeudsConnus = []
if wayToKeep:
arcsWaysOk.append(arcsWay)
else:
arcsWaysOk = []
return arcsWaysOk
def kleinArcsWay(
self,
noeudDepart: Noeud,
noeudArrivee: Noeud
) -> list:
"""Renvoie une liste :
- d'arcs de poids non nul
- et/ou d'arcs inverses de poids non nul
- ne passant pas deux fois par le même noeud
constituant un chemin entre noeudDepart et noeudArrivee s'il en existe.
Renvoie [] sinon.
Paramètres
----------
- noeudDepart : Noeud - Le noeud de départ des arcs du chemin.
- noeudArrivee : Noeud - Le noeud de d'arrivée des arcs du chemin.
Remarque
--------
Ne trouve pas les cycles.
Méthode utile pour l'algorithme de Klein.
noeudDepart : Noeud - noeud de départ
noeudArrivee : Noeud - noeud d'arrivée
Tests
-----
>>> g = Graphe('graphe')
>>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
>>> AB1,AC,AD = Arc('A->B(1)',A,B,0),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
>>> ABi = Arc('A->B(i)',A,B,1,True)
>>> BA1 = Arc('B->A(1)',B,A,0)
>>> BAi = Arc('B->A(i)',B,A,2,True)
>>> CB = Arc('C->B',C,B,1)
>>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1)
>>> EA,EB = Arc('E->A',E,A,1),Arc('E->B',E,B,1)
>>> g.setNoeuds([A,B,C,D,E])
>>> g.setArcs([AB1,ABi,AC,AD,BA1,BAi,CB,DE,DB,EA,EB])
>>> g.estSimplifie()
True
>>> # Un seul chemin de Klein de B à C
>>> for chemin in g.kleinAllArcsWays(B,C):
... print("=== chemin de Klein ===")
... for arc in chemin:
... print(arc.getNom())
...
=== chemin de Klein ===
B->A(i)
A->C
>>> # Dans ce graphe cinq chemins sont possibles entre A et B
>>> # Mais on ne veut que les chemins avec
>>> # des arcs directs de poids non nul
>>> # et/ou des arcs inverses de poids non nul
>>> # Cela n'en laisse que quatre
>>> for chemin in g.kleinAllArcsWays(A,B):
... print("=== chemin de Klein ===")
... for arc in chemin:
... print(arc.getNom())
...
=== chemin de Klein ===
A->B(i)
=== chemin de Klein ===
A->C
C->B
=== chemin de Klein ===
A->D
D->E
E->B
=== chemin de Klein ===
A->D
D->B
>>> # On fixe la graine pour en avoir un au hasard
>>> random.seed(1)
>>> for arc in g.kleinArcsWay(A,B):
... print(arc.getNom())
A->C
C->B
>>> # On remet un poids non nul sur les arcs AB1, pour retrouver les cinq chemins
>>> AB1.setPoids(1)
>>> for chemin in g.kleinAllArcsWays(A,B):
... print("=== chemin de Klein ===")
... for arc in chemin:
... print(arc.getNom())
...
=== chemin de Klein ===
A->B(1)
=== chemin de Klein ===
A->B(i)
=== chemin de Klein ===
A->C
C->B
=== chemin de Klein ===
A->D
D->E
E->B
=== chemin de Klein ===
A->D
D->B
>>> # On fixe la graine pour en avoir un au hasard
>>> random.seed(1)
>>> for arc in g.kleinArcsWay(A,B):
... print(arc.getNom())
A->B(i)
>>> # Si on mets un poids nul ailleurs que sur le premier arc d'un chemin
>>> # Le chemin doit être supprimé des possibles aussi
>>> # Cela laisse quatre cemins valides
>>> EB.setPoids(0)
>>> for chemin in g.kleinAllArcsWays(A,B):
... print("=== chemin de Klein ===")
... for arc in chemin:
... print(arc.getNom())
...
=== chemin de Klein ===
A->B(1)
=== chemin de Klein ===
A->B(i)
=== chemin de Klein ===
A->C
C->B
=== chemin de Klein ===
A->D
D->B
>>> # On a fixé la graine pour en avoir un au hasard
>>> for arc in g.kleinArcsWay(A,B):
... print(arc.getNom())
A->B(1)
""" # noqa : E501
pass
assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud." # noqa : E501
assert isinstance(noeudArrivee, Noeud), "Le noeudArrivee doit être un instance de la classe Noeud." # noqa : E501
assert noeudDepart != noeudArrivee, "noeudDepart et noeudArrivee doivent être distincts." # noqa : E501
assert self.estSimplifie(), "Les arcs directs du graphe doivent être simplifiés, fusionnés s'ils sont parallèles." # noqa : E501
arcsWayOk = []
kleinArcsWays = self.kleinAllArcsWays(noeudDepart, noeudArrivee)
if kleinArcsWays:
arcsWayOk = random.choice(kleinArcsWays)
return arcsWayOk
def kleinAllArcsCyclesFromNoeud(self, noeudDepart: Noeud) -> list:
"""Renvoie une liste de liste d'arcs représentant un cycle de Klein valide s'il en existe
Renvoie [] sinon.
Paramètres
----------
- noeudDepart : Noeud - Le noeud de départ des arcs des cycles.
Remarques
---------
- Le graphe constitué par les arcs directs doit être simplifié.
- La méthode kleinAllArcsWays() exclut déjà les chemin passant
deux fois par le même noeud.
- On va ici encore exclure certains chemins cycliques à savoir :
- ceux qui n'ont que deux arcs qui sont inverses l'un de l'autre
- ceux qui n'ont que des arcs inverses
- ceux qui ont un poids algébrique négatif.
- Le poids algébrique est obtenu en ajouter le poids des arcs directs et en enlevant le poids des arcs inverses.
Tests
-----
>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
>>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
>>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
>>> g = Graphe('graphe')
>>> g.setNoeuds([A,B,C])
>>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1])
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A'])
>>> g.estSimplifie()
False
>>> g.simplification()
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A'])
>>> g.estSimplifie()
True
>>> # Il y a 2 cycles partants de A dans ce graphe
>>> # Ce sont aussi des cycles de Klein
>>> for cycle in g.kleinAllArcsCyclesFromNoeud(A):
... print("=== cycle ===")
... for arc in cycle:
... print(arc.getNom())
...
=== cycle ===
A-7->B
B-5->C
C-9->A
=== cycle ===
A-3->C
C-9->A
>>> # On ajoute les arcs inverses manuellement pour les tests
>>> BAi, CAi,CBi,ACi = Arc('B-0->A(i)',B,A,0,True),Arc('C-1->A(i)',C,A,1,True),Arc('C-2->B(i)',C,B,2,True),Arc('A-3->C(i)',A,C,3,True)
>>> g.addArcs([BAi,CAi,CBi,ACi])
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A', 'B-0->A(i)', 'C-1->A(i)', 'C-2->B(i)', 'A-3->C(i)'])
>>> for cycle in g.kleinAllArcsCyclesFromNoeud(A):
... print("=== cycle ===")
... for arc in cycle:
... print(arc.getNom())
=== cycle ===
A-7->B
B-5->C
C-9->A
=== cycle ===
A-7->B
B-5->C
C-1->A(i)
=== cycle ===
A-3->C
C-9->A
>>> # Tests sur un autre graphe
>>> D,E,F,G = Noeud('D'),Noeud('E'),Noeud('F'),Noeud('G')
>>> DE,EF,FG,FD = Arc('D-1->E',D,E,1),Arc('E-2->F',E,F,2),Arc('F-3->G',F,G,3),Arc('F-4->D',F,D,4)
>>> # On ajoute un arc inverse manuellement
>>> GFi = Arc('G-3->F(i)',G,F,3,True)
>>> g1 = Graphe('graphe1')
>>> g1.setNoeuds([D,E,F,G])
>>> g1.setArcs([DE,EF,FG,FD,GFi])
>>> # Ne présente qu'un cycle DEFD,
>>> # Le chemin DEFGFD est exclu par la méthode kleinAllArcsWays()
>>> for cycle in g1.kleinAllArcsCyclesFromNoeud(D):
... print("=== cycle ===")
... for arc in cycle:
... print(arc.getNom())
...
=== cycle ===
D-1->E
E-2->F
F-4->D
>>> # Quelques arcs en plus qui pourraient poser problème
>>> FE,ED = Arc('F-5->E',F,E,5),Arc('E-6->D',E,D,6)
>>> FEi,EFi,DEi,EDi = Arc('F-2->E(i)',F,E,2,True),Arc('E-5->F(i)',E,F,5,True),Arc('D-6->E(i)',D,E,6,True),Arc('E-1->D(i)',E,D,1,True)
>>> g1.addArcs([FE,ED,FEi,EFi,DEi,EDi])
>>> # Il y a 2 cycles valides à partir de D
>>> for cycle in g1.kleinAllArcsCyclesFromNoeud(D):
... print("=== cycle ===")
... for arc in cycle:
... print(arc.getNom())
...
=== cycle ===
D-1->E
E-2->F
F-4->D
=== cycle ===
D-1->E
E-6->D
""" # noqa : E501
pass
# On récupère tous les chemins cycliques
allArcsKleinCycles = []
if self.kleinHasCycleFromNoeud(noeudDepart):
noeudsPossibles = []
for noeud in self.getCreanciers(noeudDepart):
if (self.kleinAllArcsWays(noeud, noeudDepart) and
noeud not in noeudsPossibles):
noeudsPossibles.append(noeud)
# Pour tous les noeuds qui permettent de revenir à noeudDepart
for noeudPossible in noeudsPossibles:
arcsKleinCycle = []
# Pour tous les chemins entre un noeudPossible et noeudDepart
suiteChemin = self.kleinAllArcsWays(noeudPossible, noeudDepart) # noqa : E501
for chemin in suiteChemin:
# pour tous les arcs qui permettent de joindre noeudDepart
# et noeudPossible
debutChemin = self.kleinArcsBetween(noeudDepart, noeudPossible) # noqa : E501
for premierArc in debutChemin:
# On remet à zéro
arcsKleinCycle = []
# On ajoute le premier arc
arcsKleinCycle.extend([premierArc])
# On ajoute le reste du chemin
arcsKleinCycle.extend(chemin)
# On évite les doublons
if arcsKleinCycle not in allArcsKleinCycles:
allArcsKleinCycles.append(arcsKleinCycle)
# Il faut encore nettoyer de certains chemins :
# -> ceux qui n'ont que deux arcs qui sont inverses
# l'un de l'autre
# -> ceux qui n'ont que des arcs inverses
# -> ceux qui ont un poids algébrique négatif.
# ======== Remarque
# On a besoin d'un variable globale pour éviter l'erreur
# UnboundLocalError: local variable referenced before assignment python # noqa : E501
# ========
global allArcsKleinCyclesCleaned
allArcsKleinCyclesCleaned = []
for arcsKleinCycle in allArcsKleinCycles:
cycleToKeep = True
# ceux qui n'ont que deux arcs qui sont inverses
# l'un de l'autre
if len(arcsKleinCycle) == 2:
arc0 = arcsKleinCycle[0]
arc1 = arcsKleinCycle[1]
if self.hasArcInverse(arc1):
if arc0 == self.getArcInverse(arc1):
cycleToKeep = False
if self.hasArcInverse(arc0):
if arc1 == self.getArcInverse(arc0):
cycleToKeep = False
# ceux qui n'ont que des arcs inverses
onlyArcsInverses = True
for arc in arcsKleinCycle:
if not arc.getEstInverse():
onlyArcsInverses = False
if onlyArcsInverses:
cycleToKeep = False
# ceux qui ont un poids algébrique négatif
cycleWeight = 0
for arc in arcsKleinCycle:
if arc.getEstInverse():
cycleWeight -= arc.getPoids()
if not arc.getEstInverse():
cycleWeight += arc.getPoids()
if cycleWeight <= 0:
cycleToKeep = False
if cycleToKeep:
allArcsKleinCyclesCleaned.append(arcsKleinCycle)
return allArcsKleinCyclesCleaned
def kleinArcsCycleFromNoeud(self, noeudDepart: Noeud) -> list:
"""Renvoie une liste d'arcs représentant un cycle de Klein partant du noeudDepart s'il en existe.
Renvoie [] sinon.
Paramètres
----------
- noeudDepart : Noeud - Le noeud de départ des arcs du cycle.
Remarques
---------
- S'il existe plusieurs cycles à partir d'un noeud, l'algorithme n'en renvoie qu'un seul au hasard.
- La recherche de cycles de Klein sous-entend que le graphe est simplifié, c'est à dire que les arcs
parallèles entre deux noeuds soient fucionnés.
- Un cycle ne doit pas passer plusieurs fois par le même noeud
Tests
-----
>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
>>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
>>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
>>> g = Graphe('graphe')
>>> g.setNoeuds([A,B,C])
>>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1])
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A'])
>>> g.estSimplifie()
False
>>> g.simplification()
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A'])
>>> g.estSimplifie()
True
>>> # Il y a 2 cycles partants de A dans ce graphe
>>> # Ce sont aussi des cycles de Klein
>>> # On les récupère tous en fixant la graine
>>> random.seed(1)
>>> for arc in g.kleinArcsCycleFromNoeud(A):
... print(arc.getNom())
...
A-7->B
B-5->C
C-9->A
>>> random.seed(5)
>>> for arc in g.kleinArcsCycleFromNoeud(A):
... print(arc.getNom())
...
A-3->C
C-9->A
>>> # On ajoute les arcs inverses manuellement pour les tests
>>> BAi, CAi,CBi,ACi = Arc('B-0->A(i)',B,A,0,True),Arc('C-1->A(i)',C,A,1,True),Arc('C-2->B(i)',C,B,2,True),Arc('A-3->C(i)',A,C,3,True)
>>> g.addArcs([BAi,CAi,CBi,ACi])
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A', 'B-0->A(i)', 'C-1->A(i)', 'C-2->B(i)', 'A-3->C(i)'])
>>> # En fixant la graine on teste pour vérifier qu'on obtient bien tous les cycles
>>> for cycle in g.kleinAllArcsCyclesFromNoeud(A):
... print("=== cycle ===")
... for arc in cycle:
... print(arc.getNom())
...
=== cycle ===
A-7->B
B-5->C
C-9->A
=== cycle ===
A-7->B
B-5->C
C-1->A(i)
=== cycle ===
A-3->C
C-9->A
>>> random.seed(1)
>>> for arc in g.kleinArcsCycleFromNoeud(A):
... print(arc.getNom())
...
A-7->B
B-5->C
C-9->A
>>> random.seed(5)
>>> for arc in g.kleinArcsCycleFromNoeud(A):
... print(arc.getNom())
...
A-3->C
C-9->A
>>> random.seed(7)
>>> for arc in g.kleinArcsCycleFromNoeud(A):
... print(arc.getNom())
...
A-7->B
B-5->C
C-1->A(i)
""" # noqa : E501
pass
assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud." # noqa : E501
arcsKleinCycle = []
if self.kleinHasCycleFromNoeud(noeudDepart):
allCycles = self.kleinAllArcsCyclesFromNoeud(noeudDepart)
arcsKleinCycle = random.choice(allCycles)
return arcsKleinCycle
def kleinAllArcsCycles(self) -> list:
"""Renvoie tous les cycles d'un graphe sans doublons.
Remarques
---------
- Notamment deux cycles sont considérés comme identiques s'ils
contiennent globalement les mêmes arcs.
Tests
-----
>>> A, B, C = Noeud('A'), Noeud('B'), Noeud('C')
>>> D, E, F = Noeud('D'), Noeud('E'), Noeud('F')
>>> kleinAB, kleinBC = Arc('A-4->B', A, B, 4), Arc('B-6->C', B, C, 6)
>>> kleinCD, kleinDE = Arc('C-5->D', C, D, 5), Arc('D-4->E', D, E, 4)
>>> kleinEF, kleinFA = Arc('E-7->F', E, F, 7), Arc('F-5->A', F, A, 5)
>>> kleinAE, kleinBE = Arc('A-2->E', A, E, 2), Arc('B-2->E', B, E, 2)
>>> kleinDB, kleinFC = Arc('D-2->B', D, B, 2), Arc('F-3->C', F, C, 3)
>>> G3 = Graphe('graphe3')
>>> G3.setNoeuds([A, B, C, D, E, F])
>>> G3.setArcs([kleinAB, kleinBC, kleinCD, kleinDE, kleinEF])
>>> G3.addArcs([kleinFA, kleinAE, kleinBE, kleinDB, kleinFC])
>>> # Ce graphe compte 25 cycles si on n'élimine pas les doublons
>>> # Mais 6 cycles distincts
>>> print(len(G3.kleinAllArcsCycles()))
6
>>> for cycle in G3.kleinAllArcsCycles():
... print('=== cycle unique ===')
... for arc in cycle:
... print(arc.getNom())
...
=== cycle unique ===
A-4->B
B-6->C
C-5->D
D-4->E
E-7->F
F-5->A
=== cycle unique ===
A-4->B
B-2->E
E-7->F
F-5->A
=== cycle unique ===
A-2->E
E-7->F
F-5->A
=== cycle unique ===
B-6->C
C-5->D
D-2->B
=== cycle unique ===
B-2->E
E-7->F
F-3->C
C-5->D
D-2->B
=== cycle unique ===
C-5->D
D-4->E
E-7->F
F-3->C
"""
pass
# On récupère tous les noeuds
noeuds = self.getNoeuds()
# On récupère les cycles au fur et à mesure
cyclesSansDoublons = []
# On initialise avec tous les cycles du premier noeud
for cycle in self.kleinAllArcsCyclesFromNoeud(noeuds[0]):
cyclesSansDoublons.append(cycle)
# Pour chaque noeuds
for noeud in noeuds:
# Pour chaque cycle partant du noeud
for cycle in self.kleinAllArcsCyclesFromNoeud(noeud):
toAdd = True
for cycleUnique in cyclesSansDoublons:
if collections.Counter(cycle) == collections.Counter(cycleUnique): # noqa : E501
toAdd = False
if toAdd:
cyclesSansDoublons.append(cycle)
return cyclesSansDoublons
def kleinEstCycle(self, arcsCycle: list) -> bool:
"""Renvoie un booléen selon que arcsCycle est un cycle de Klein du
graphe ou non.
Remarque
--------
On n'utilise pas la méthode kleinAllArcsCycles() ici car elle exclut
les doublons or tous les cycles doivent être testés par exemple
ABCA et BCAB sont un doublon du même cycle mais doivent être testés
tous les deux.
Paramètres
----------
arcsCycle : list - Une liste d'arcs candidate à être un cycle de Klein.
Tests
-----
>>> A, B, C = Noeud('A'), Noeud('B'), Noeud('C')
>>> D, E, F = Noeud('D'), Noeud('E'), Noeud('F')
>>> kleinAB, kleinBC = Arc('A-4->B', A, B, 4), Arc('B-6->C', B, C, 6)
>>> kleinCD, kleinDE = Arc('C-5->D', C, D, 5), Arc('D-4->E', D, E, 4)
>>> kleinEF, kleinFA = Arc('E-7->F', E, F, 7), Arc('F-5->A', F, A, 5)
>>> kleinAE, kleinBE = Arc('A-2->E', A, E, 2), Arc('B-2->E', B, E, 2)
>>> kleinDB, kleinFC = Arc('D-2->B', D, B, 2), Arc('F-3->C', F, C, 3)
>>> G3 = Graphe('graphe3')
>>> G3.setNoeuds([A, B, C, D, E, F])
>>> G3.setArcs([kleinAB, kleinBC, kleinCD, kleinDE, kleinEF])
>>> G3.addArcs([kleinFA, kleinAE, kleinBE, kleinDB, kleinFC])
>>> cycleOK = [kleinAB, kleinBC, kleinCD, kleinDE, kleinEF, kleinFA]
>>> cycleKO = [kleinDB, kleinBE, Arc('E-1->D', E, D, 1)]
>>> G3.kleinEstCycle(cycleOK)
True
>>> G3.kleinEstCycle(cycleKO)
False
"""
pass
for arc in arcsCycle:
assert isinstance(arc, Arc), "Tous les éléments de arcsCycle doivent être des instances de la classe Arc." # noqa : E501
# booléen de sortie
boolDeSortie = True
# On récupère tous les cycles du graphe
cyclesDuGraphe = []
for noeud in self.getNoeuds():
for cycle in self.kleinAllArcsCyclesFromNoeud(noeud):
cyclesDuGraphe.append(cycle)
if arcsCycle not in cyclesDuGraphe:
boolDeSortie = False
return boolDeSortie
def kleinModifierCycle(self, arcsCycle: list) -> None:
"""Renvoie le graphe modifié après réduction du cycle.
Remarque
--------
- Chaque arc dans le sens du cycle est modifié en diminuant son poids
du plus petit poids des arcs du cycle.
- Chaque arc dans le sens inverse du cycle est aussi modifié en augmentant
son poids du plus petit poids des arcs du cycle.
Paramètres
----------
arcsCycle : list - Le cycle de Klein à réduire.
Tests
-----
>>> A, B, C = Noeud('A'), Noeud('B'), Noeud('C')
>>> D, E, F = Noeud('D'), Noeud('E'), Noeud('F')
>>> kleinAB, kleinBC = Arc('A-4->B', A, B, 4), Arc('B-6->C', B, C, 6)
>>> kleinCD, kleinDE = Arc('C-5->D', C, D, 5), Arc('D-4->E', D, E, 4)
>>> kleinEF, kleinFA = Arc('E-7->F', E, F, 7), Arc('F-5->A', F, A, 5)
>>> kleinAE, kleinBE = Arc('A-2->E', A, E, 2), Arc('B-2->E', B, E, 2)
>>> kleinDB, kleinFC = Arc('D-2->B', D, B, 2), Arc('F-3->C', F, C, 3)
>>> G3 = Graphe('graphe3')
>>> G3.setNoeuds([A, B, C, D, E, F])
>>> G3.setArcs([kleinAB, kleinBC, kleinCD, kleinDE, kleinEF])
>>> G3.addArcs([kleinFA, kleinAE, kleinBE, kleinDB, kleinFC])
>>> G3.addArcsInversesNuls()
>>> cycleOK = [kleinAB, kleinBC, kleinCD, kleinDE, kleinEF, kleinFA]
>>> for arc in cycleOK:
... print(arc.getNom())
...
A-4->B
B-6->C
C-5->D
D-4->E
E-7->F
F-5->A
>>> G3.kleinModifierCycle(cycleOK)
>>> for arc in cycleOK:
... print(arc.getNom())
...
A-0->B
B-2->C
C-1->D
D-0->E
E-3->F
F-1->A
""" # noqa : E501
pass
assert self.kleinEstCycle(arcsCycle), "arcsCycle doit être un cycle de Klein du Graphe." # noqa : E501
for arc in arcsCycle:
assert isinstance(arc, Arc), "Tous les éléments de arcsCycle doivent être des instances de la classe Arc." # noqa : E501
minPoids = self.getMinPoidsFromCycle(arcsCycle)
for arc in arcsCycle:
# On diminue le poids de l'arc
arc.setPoids(arc.getPoids()-minPoids)
# On augmente le poids de son arc inverse
arcInverse = self.getArcInverse(arc)
arcInverse.setPoids(arcInverse.getPoids()+minPoids)
# On renomme les arcs
nouveauNom = arc.getNoeudDebut().getNom()+'-'+str(arc.getPoids())+'->'+arc.getNoeudFin().getNom() # noqa : E501
arc.setNom(nouveauNom)
nouveauNomInverse = arcInverse.getNoeudDebut().getNom()+'-'+str(arcInverse.getPoids())+'->'+arcInverse.getNoeudFin().getNom() # noqa : E501
arcInverse.setNom(nouveauNomInverse)
def kleinModifierCycleFromNoeud(self, noeudCandidat: Noeud) -> None:
"""Modifie un cycle de Klein pour un noeud du graphe s'il en existe,
sinon le graphe n'est pas modifié.
- Chaque arc dans le sens du cycle est modifié en diminuant son poids
du plus petit poids des arcs du cycle.
- Chaque arc dans le sens inverse du cycle est aussi modifié en augmentant
son poids du plus petit poids des arcs du cycle.
Renvoie l'instance du graphe modifiée ou pas selon l'existence d'un
cycle de Klein.
Paramètres
----------
noeudCandidat : Noeud - Une instance de la classe Noeud.
Remarque
--------
- Un cycle de Klein est un cycle :
- dont tous les arcs ont un poids non nul.
- dont le poids algébrique est strictement positif.
- Le poids algébrique est obtenu en ajoutant le poids des arcs directs et en enlevant le poids des arcs inverses.
Tests
-----
>>> g = Graphe('graphe')
>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
>>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
>>> BC5 = Arc('B-5->C',B,C,5)
>>> CA8,CA1 = Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
>>> g.setNoeuds([A,B,C])
>>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1])
>>> # On simplifie le graphe
>>> g.simplification()
>>> for arc in g.getArcs():
... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())
...
A-7->B => arc inverse : False
A-3->C => arc inverse : False
B-5->C => arc inverse : False
C-9->A => arc inverse : False
>>> # On ajoute les arcs inverses
>>> g.addArcsInversesNuls()
>>> for arc in g.getArcs():
... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())
...
A-7->B => arc inverse : False
A-3->C => arc inverse : False
B-5->C => arc inverse : False
C-9->A => arc inverse : False
B-0->A => arc inverse : True
C-0->A => arc inverse : True
C-0->B => arc inverse : True
A-0->C => arc inverse : True
>>> # Ce graphe présente deux cycles : ABCA et ACA
>>> # On modifie ACA
>>> random.seed(11)
>>> g.kleinModifierCycleFromNoeud(A)
>>> for arc in g.getArcs():
... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())
...
A-7->B => arc inverse : False
A-0->C => arc inverse : False
B-5->C => arc inverse : False
C-6->A => arc inverse : False
B-0->A => arc inverse : True
C-3->A => arc inverse : True
C-0->B => arc inverse : True
A-3->C => arc inverse : True
>>> # Il ne reste qu'un cycle ABCA, ACA est exclu car constitué par un arc et son propre inverse
>>> for cycle in g.kleinAllArcsCyclesFromNoeud(A):
... print("=== cycle ===")
... for arc in cycle:
... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())
...
=== cycle ===
A-7->B => arc inverse : False
B-5->C => arc inverse : False
C-6->A => arc inverse : False
=== cycle ===
A-7->B => arc inverse : False
B-5->C => arc inverse : False
C-3->A => arc inverse : True
""" # noqa : E501
pass
assert isinstance(noeudCandidat, Noeud), "Le noeudCandidat doit être un instance de la classe Noeud." # noqa : E501
# Pour chaque arc du cycle, il doit y avoir un arc inverse
for arc in self.getArcs():
assert self.hasArcInverse(arc), "Tous les arcs du graphe doivent avoir un arc inverse" # noqa : E501
if self.kleinHasCycleFromNoeud(noeudCandidat):
# On choisit un cycle à réduire au hasard
cycleToChange = random.choice(
self.kleinAllArcsCyclesFromNoeud(noeudCandidat)
)
self.kleinModifierCycle(cycleToChange)
def kleinDeleteDettesNulles(self) -> None:
"""Supprime tous les arcs de poids nul.
Tests
-----
>>> g = Graphe('graphe')
>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
>>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
>>> BC5 = Arc('B-5->C',B,C,5)
>>> CA8,CA1 = Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
>>> g.setNoeuds([A,B,C])
>>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1])
>>> # On simplifie le graphe
>>> g.simplification()
>>> for arc in g.getArcs():
... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())
...
A-7->B => arc inverse : False
A-3->C => arc inverse : False
B-5->C => arc inverse : False
C-9->A => arc inverse : False
>>> # On ajoute les arcs inverses
>>> g.addArcsInversesNuls()
>>> random.seed(1)
>>> # Ce graphe présente deux cycles : ABCA et ACA
>>> # On modifie ABCA
>>> g.kleinModifierCycleFromNoeud(A)
>>> for arc in g.getArcs():
... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())
...
A-2->B => arc inverse : False
A-3->C => arc inverse : False
B-0->C => arc inverse : False
C-4->A => arc inverse : False
B-5->A => arc inverse : True
C-0->A => arc inverse : True
C-5->B => arc inverse : True
A-5->C => arc inverse : True
>>> # On supprime les arcs de poids nul
>>> g.kleinDeleteDettesNulles()
>>> for arc in g.getArcs():
... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())
...
A-2->B => arc inverse : False
A-3->C => arc inverse : False
C-4->A => arc inverse : False
B-5->A => arc inverse : True
C-5->B => arc inverse : True
A-5->C => arc inverse : True
""" # noqa : E501
pass
# On récupère d'abord tous les arcs à supprimer
# Car la suppression de proche en proche peut laisser
# un arc à l'écart à cause de la modification du graphe
arcsToDelete = []
for arc in self.getArcs():
if arc.getPoids() == 0:
arcsToDelete.append(arc)
# On supprime alors tous les arcs à supprimer
for arc in arcsToDelete:
self.removeArc(arc)
def kleinDeleteArcsInverses(self) -> None:
"""Supprime tous les arcs inverses temporaires.
Tests
-----
>>> g = Graphe('graphe')
>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C')
>>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3)
>>> BC5 = Arc('B-5->C',B,C,5)
>>> CA8,CA1 = Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1)
>>> g.setNoeuds([A,B,C])
>>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1])
>>> # On simplifie le graphe
>>> g.simplification()
>>> for arc in g.getArcs():
... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())
...
A-7->B => arc inverse : False
A-3->C => arc inverse : False
B-5->C => arc inverse : False
C-9->A => arc inverse : False
>>> # On ajoute les arcs inverses
>>> g.addArcsInversesNuls()
>>> for arc in g.getArcs():
... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())
...
A-7->B => arc inverse : False
A-3->C => arc inverse : False
B-5->C => arc inverse : False
C-9->A => arc inverse : False
B-0->A => arc inverse : True
C-0->A => arc inverse : True
C-0->B => arc inverse : True
A-0->C => arc inverse : True
>>> random.seed(1)
>>> # Ce graphe présente deux cycles : ABCA et ACA
>>> # On modifie ABCA
>>> g.kleinModifierCycleFromNoeud(A)
>>> for arc in g.getArcs():
... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())
...
A-2->B => arc inverse : False
A-3->C => arc inverse : False
B-0->C => arc inverse : False
C-4->A => arc inverse : False
B-5->A => arc inverse : True
C-0->A => arc inverse : True
C-5->B => arc inverse : True
A-5->C => arc inverse : True
>>> # On supprime les arcs de poids nul
>>> g.kleinDeleteDettesNulles()
>>> for arc in g.getArcs():
... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())
...
A-2->B => arc inverse : False
A-3->C => arc inverse : False
C-4->A => arc inverse : False
B-5->A => arc inverse : True
C-5->B => arc inverse : True
A-5->C => arc inverse : True
>>> # On supprime les arcs inverses temporaires
>>> g.kleinDeleteArcsInverses()
>>> for arc in g.getArcs():
... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse())
...
A-2->B => arc inverse : False
A-3->C => arc inverse : False
C-4->A => arc inverse : False
""" # noqa : E501
pass
# On récupère d'abord tous les arcs à supprimer
# Car la suppression de proche en proche peut laisser
# un arc à l'écart à cause de la modification du graphe
arcsToDelete = []
for arc in self.getArcs():
if arc.getEstInverse():
arcsToDelete.append(arc)
# On supprime alors tous les arcs à supprimer du graphe
for arc in arcsToDelete:
self.removeArc(arc)
# ================================================================
# =========== METHODES NON UTILISÉES =============================
# ================================================================
def getNodesCycleFromNoeud(self, noeudDepart: Noeud) -> list:
"""Renvoie une liste de noeuds représentant un cycle partant du noeudDepart s'il en existe.
[] sinon.
Paramètres
----------
noeudDepart : Noeud - Le noeud de départ du cycle.
Remarque
--------
S'il existe plusieurs cycles à partir d'un noeud, l'algorithme n'en renvoie qu'un seul choisi au hasard.
Tests
-----
>>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E')
>>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1)
>>> BA = Arc('B->A',B,A,1)
>>> CB = Arc('C->B',C,B,1)
>>> DE = Arc('D->E',D,E,2)
>>> EA = Arc('E->A',E,A,3)
>>> g = Graphe('graphe')
>>> g.setNoeuds([A,B,C,D,E])
>>> g.setArcs([AB,AC,AD,CB,DE,EA])
>>> print(g)
Graphe(nom=graphe, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'A->D', 'C->B', 'D->E', 'E->A'])
>>> for e in g.getNodesCycleFromNoeud(A):
... print(e.getNom())
...
A
D
E
A
>>> g.getNodesCycleFromNoeud(B)
[]
>>> g1 = Graphe('graphe1')
>>> g1.setNoeuds([A,B,C,D,E])
>>> g1.setArcs([AB,AC,BA,CB,DE,EA])
>>> print(g1)
Graphe(nom=graphe1, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'B->A', 'C->B', 'D->E', 'E->A'])
>>> # Il y a deux cycles partants de A dans ce graphe
>>> random.seed(3)
>>> for e in g1.getNodesCycleFromNoeud(A):
... print(e.getNom())
...
A
B
A
>>> random.seed(7)
>>> for e in g1.getNodesCycleFromNoeud(A):
... print(e.getNom())
...
A
C
B
A
""" # noqa : E501
pass
assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud." # noqa : E501
cycle = []
if self.hasCycleFromNoeud(noeudDepart):
cycle.append(noeudDepart)
noeudsPossibles = []
for noeud in self.getCreanciers(noeudDepart):
if self.getNodesWays(noeud, noeudDepart):
noeudsPossibles.append(noeud)
noeudAleatoire = random.choice(noeudsPossibles)
cheminsPossibles = self.getNodesWays(noeudAleatoire, noeudDepart)
cheminAleatoire = random.choice(cheminsPossibles)
cycle.extend(cheminAleatoire)
return cycle
def kleinBestArcsCycle(self) -> list:
"""Renvoie le liste d'arcs réduisant au maximum la dette globale d'un graphe.
Remarques
---------
- Il semble qu'en choisissant à chaque étape le cycle qui réduit
au maximum la dette globale, la réduction finale ne soit pas la
meilleure.
- Cette fonction est donc incorrecte
Tests
-----
>>> A, B, C = Noeud('A'), Noeud('B'), Noeud('C')
>>> D, E, F = Noeud('D'), Noeud('E'), Noeud('F')
>>> kleinAB, kleinBC = Arc('A-4->B', A, B, 4), Arc('B-6->C', B, C, 6)
>>> kleinCD, kleinDE = Arc('C-5->D', C, D, 5), Arc('D-4->E', D, E, 4)
>>> kleinEF, kleinFA = Arc('E-7->F', E, F, 7), Arc('F-5->A', F, A, 5)
>>> kleinAE, kleinBE = Arc('A-2->E', A, E, 2), Arc('B-2->E', B, E, 2)
>>> kleinDB, kleinFC = Arc('D-2->B', D, B, 2), Arc('F-3->C', F, C, 3)
>>> G3 = Graphe('graphe3')
>>> G3.setNoeuds([A, B, C, D, E, F])
>>> G3.setArcs([kleinAB, kleinBC, kleinCD, kleinDE, kleinEF])
>>> G3.addArcs([kleinFA, kleinAE, kleinBE, kleinDB, kleinFC])
>>> # Ce graphe compte 25 cycles si on n'élimine pas les doublons
>>> # Mais 6 cycles distincts
>>> print(len(G3.kleinAllArcsCycles()))
6
>>> G3.addArcsInversesNuls()
>>> bestCycle1 = G3.kleinBestArcsCycle()
>>> for arc in bestCycle1:
... print(arc.getNom())
...
A-4->B
B-6->C
C-5->D
D-4->E
E-7->F
F-5->A
>>> G3.kleinModifierCycle(bestCycle1)
>>> bestCycle2 = G3.kleinBestArcsCycle()
>>> for arc in bestCycle2:
... print(arc.getNom())
...
B-2->E
E-3->F
F-3->C
C-4->B
"""
pass
# On récupère tous les cycles sans doublons
allCycles = self.kleinAllArcsCycles()
bestCycle = []
reductionDeLaDette = 0
for cycle in allCycles:
reductionDuCycle = len(cycle)*self.getMinPoidsFromCycle(cycle)
if reductionDuCycle > reductionDeLaDette:
reductionDeLaDette = reductionDuCycle
bestCycle = []
bestCycle.extend(cycle)
return bestCycle
def colorierGrapheFromNoeud(self, noeudDepart: Noeud) -> tuple:
"""Renvoie un tuple contenant deux dictionnaires issu d'un parcours en profondeur :
- 1er : Clef : nom d'un noeud - Valeur : père du noeud dans le parcours choisi
- 2e : Clef : nom d'un noeud - Valeur : couleur du noeud
blanc : non visité
gris : en cours de visite
noir : visité
Remarques
---------
- J'ai codé cette fonction à un moment dans mes errances mais je ne suis pas sûr de m'en servir.
Toutefois je ne veux pas perdre le code pour le moment.
- La méthode ne s'occupe que de faire les coloriages à partir de noeudDepart, il faudrait la rappeler recursivement
pour qu'elle traite tout le graphe.
- L'algorithme du parcours n'est ni aléatoire ni exhaustif, il ne traite pas tous les cas possibles.
- À partir du noeudDepart, on va sur un sommet blanc tant que c'est possible, on le colorie en gris
tant que le traitement n'est pas fini c'est à dire qu'on ne dépile pas. Quand il n'y a plus de noeud blanc
accessible on dépile.
Tests
-----
>>> A,B,C,D = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D')
>>> E,F,G,H = Noeud('E'),Noeud('F'),Noeud('G'),Noeud('H')
>>> AC,AD = Arc('A->C',A,C,1),Arc('A->D',A,D,1)
>>> BD,BF = Arc('B->D',B,D,1),Arc('B->F',B,F,1)
>>> CF,CB,CD = Arc('C->F',C,F,1),Arc('C->B',C,B,1),Arc('C->D',C,D,1)
>>> DE,DG = Arc('D->E',D,E,1),Arc('D->G',D,G,1)
>>> FH = Arc('F->H',F,H,1)
>>> GE = Arc('G->E',G,E,1)
>>> HB = Arc('H->B',H,B,1)
>>> g = Graphe('graphe')
>>> g.setNoeuds([A,B,C,D,E,F,G,H])
>>> g.setArcs([AC,AD,BD,BF,CF,CB,CD,DE,DG,FH,GE,HB])
>>> # Dictionnaire des pères à partir de D
>>> g.colorierGrapheFromNoeud(D)[0]
{'D': None, 'E': 'D', 'G': 'D'}
>>> # Dictionnaire du coloriage partir de D
>>> g.colorierGrapheFromNoeud(D)[1]
{'A': 'blanc', 'B': 'blanc', 'C': 'blanc', 'D': 'noir', 'E': 'noir', 'F': 'blanc', 'G': 'noir', 'H': 'blanc'}
>>> # Dictionnaire des pères à partir de C
>>> g.colorierGrapheFromNoeud(C)[0]
{'C': None, 'F': 'C', 'H': 'F', 'B': 'H', 'D': 'B', 'E': 'D', 'G': 'D'}
>>> # Dictionnaire du coloriage partir de C
>>> g.colorierGrapheFromNoeud(C)[1]
{'A': 'blanc', 'B': 'noir', 'C': 'noir', 'D': 'noir', 'E': 'noir', 'F': 'noir', 'G': 'noir', 'H': 'noir'}
""" # noqa : E501
pass
assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud." # noqa : E501
pile = []
parents = {}
couleurs = {}
for noeud in self.getNoeuds():
couleurs[noeud.getNom()] = 'blanc'
parents[noeudDepart.getNom()] = None
couleurs[noeudDepart.getNom()] = 'gris'
pile.append(noeudDepart)
while pile != []:
pere = pile[-1]
descendants = []
for noeud in self.getCreanciers(pere):
if couleurs[noeud.getNom()] == 'blanc':
descendants.append(noeud)
if descendants != []:
fils = descendants[0]
couleurs[fils.getNom()] = 'gris'
parents[fils.getNom()] = pere.getNom()
pile.append(fils)
else:
pile.pop()
couleurs[pere.getNom()] = 'noir'
return parents, couleurs
if __name__ == "__main__":
import doctest
doctest.testmod()
Classes
class Graphe (nom: str, noeuds=None, arcs=None)
-
La classe Graphe sert pour créer un graphe.
Attributs
-
nom : str - Identification du graphe par un nom.
-
noeuds : list - Les noeuds du graphe stockés dans une list.
-
arcs : list - Les arcs du graphe stockés dans une list.
Constructeur
Tests
>>> g1 = Graphe('graphe1') >>> print(g1) Graphe(nom=graphe1, noeuds=[], arcs=[]) >>> A = Noeud('A') >>> B = Noeud('B') >>> g1.addNoeud(A) >>> g1.addNoeud(B) >>> a1 = Arc('A-1->B', A, B, 1) >>> a2 = Arc('A-2->B(i)', A, B, 2, True) >>> g1.addArc(a1) >>> g1.addArc(a2) >>> print(g1) Graphe(nom=graphe1, noeuds=['A', 'B'], arcs=['A-1->B', 'A-2->B(i)'])
Expand source code
class Graphe: """La classe Graphe sert pour créer un graphe. Attributs --------- - nom : str - Identification du graphe par un nom. - noeuds : list - Les noeuds du graphe stockés dans une list. - arcs : list - Les arcs du graphe stockés dans une list. """ def __init__(self, nom: str, noeuds=None, arcs=None) -> None: """Constructeur Tests ----- >>> g1 = Graphe('graphe1') >>> print(g1) Graphe(nom=graphe1, noeuds=[], arcs=[]) >>> A = Noeud('A') >>> B = Noeud('B') >>> g1.addNoeud(A) >>> g1.addNoeud(B) >>> a1 = Arc('A-1->B', A, B, 1) >>> a2 = Arc('A-2->B(i)', A, B, 2, True) >>> g1.addArc(a1) >>> g1.addArc(a2) >>> print(g1) Graphe(nom=graphe1, noeuds=['A', 'B'], arcs=['A-1->B', 'A-2->B(i)']) """ pass assert isinstance(nom, str), "Le nom d'un graphe doit être un string." assert isinstance(noeuds, list) or noeuds is None, "Les noeuds d'un graphe sont soit une liste soit None." # noqa: E501 assert isinstance(arcs, list) or arcs is None, "Les arcs d'un graphe sont soit une liste soit None." # noqa: E501 self.nom = nom if noeuds is None: self.noeuds = [] else: self.noeuds = noeuds if arcs is None: self.arcs = [] else: self.arcs = arcs def __str__(self) -> None: """Représentation en chaine de caractères d'une instance de la classe Graphe. Tests ----- >>> g1 = Graphe('graphe1') >>> print(g1) Graphe(nom=graphe1, noeuds=[], arcs=[]) >>> A = Noeud('A') >>> B = Noeud('B') >>> g1.addNoeud(A) >>> g1.addNoeud(B) >>> a1 = Arc('A-1->B', A, B, 1) >>> a2 = Arc('A-2->B(i)', A, B, 2, True) >>> g1.addArc(a1) >>> g1.addArc(a2) >>> print(g1) Graphe(nom=graphe1, noeuds=['A', 'B'], arcs=['A-1->B', 'A-2->B(i)']) """ # noqa: E501 pass noeuds = [] for noeud in self.noeuds: noeuds.append(noeud.nom) arcs = [] for arc in self.arcs: arcs.append(arc.nom) return "Graphe(nom={}, noeuds={}, arcs={})".format( self.nom, noeuds, arcs ) # ================================================================ # =========== GETTERS ============================================ # ================================================================ def getNom(self) -> str: """Renvoie le nom du graphe. Tests ----- >>> g1 = Graphe('graphe1') >>> g1.getNom() 'graphe1' """ pass return self.nom def getNoeuds(self) -> list: """Renvoie le tableau des noeuds du graphe. Tests ----- >>> g1 = Graphe('graphe1') >>> n1 = Noeud('n1') >>> n2 = Noeud('n2') >>> n3 = Noeud('n3') >>> g1.addNoeud(n1) >>> g1.addNoeud(n2) >>> g1.addNoeud(n3) >>> for noeud in g1.getNoeuds(): ... print(noeud) ... Noeud(nom=n1, voisins=[], nbArcs=0, capital=0) Noeud(nom=n2, voisins=[], nbArcs=0, capital=0) Noeud(nom=n3, voisins=[], nbArcs=0, capital=0) """ pass return self.noeuds def getArcs(self) -> list: """Renvoie le tableau des arcs du graphe. Tests ----- >>> g1 = Graphe('graphe1') >>> n1 = Noeud('n1') >>> n2 = Noeud('n2') >>> n3 = Noeud('n3') >>> g1.addNoeud(n1) >>> g1.addNoeud(n2) >>> g1.addNoeud(n3) >>> a1 = Arc('n1n2_0', n1, n2, 1) >>> a2 = Arc('n1n2_1', n1, n2, 2) >>> a3 = Arc('n1n3_0', n1, n3, 3) >>> a4 = Arc('n2n3_0', n2, n3, 4) >>> g1.addArc(a1) >>> g1.addArc(a2) >>> g1.addArc(a3) >>> g1.addArc(a4) >>> for arc in g1.getArcs(): ... # On n'affiche que les noms des arcs ... print(arc.nom) ... n1n2_0 n1n2_1 n1n3_0 n2n3_0 >>> for noeud in g1.getNoeuds(): ... print(noeud) ... Noeud(nom=n1, voisins=['n2', 'n3'], nbArcs=3, capital=0) Noeud(nom=n2, voisins=['n1', 'n3'], nbArcs=3, capital=0) Noeud(nom=n3, voisins=['n1', 'n2'], nbArcs=2, capital=0) """ pass return self.arcs # ================================================================ # =========== SETTERS ============================================ # ================================================================ def setNom(self, nouveauNom) -> str: """Redéfinit le nom du graphe. Paramètres ---------- nouveauNom : str - Le nouveau nom du graphe Tests ----- >>> g1 = Graphe('graphe1') >>> g1.getNom() 'graphe1' >>> g1.setNom('new_graphe1') >>> g1.getNom() 'new_graphe1' """ pass assert isinstance(nouveauNom, str), "Le nom d'un graphe doit être un string." # noqa : E501 self.nom = nouveauNom def setNoeuds(self, nouveauxNoeuds: list) -> None: """Définit le tableau des noeuds d'une instance de la classe Graphe. Paramètres ---------- nouveauxNoeuds : list - Une liste d'instances de la classe Noeuds. Remarque: --------- On ne définit les noeuds d'un graphe que si le graphe n'a aucun noeud. Tests ----- >>> g1 = Graphe('graphe1') >>> print(g1) Graphe(nom=graphe1, noeuds=[], arcs=[]) >>> n1 = Noeud('n1') >>> n2 = Noeud('n2') >>> n3 = Noeud('n3') >>> nouveauxNoeuds = [n1, n2, n3] >>> g1.setNoeuds(nouveauxNoeuds) >>> print(g1) Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=[]) """ pass # Un booléen pour tester si tout est OK allOk = True for nouveauNoeud in nouveauxNoeuds: assert nouveauNoeud not in self.noeuds, "On ne peut ajouter un noeud déjà présent." # noqa : E501 if nouveauNoeud in self.noeuds: allOk = False raise ValueError("On ne peut ajouter un noeud déjà présent.") if allOk: for noeud in nouveauxNoeuds: self.addNoeud(noeud) def setArcs(self, nouveauxArcs: list) -> None: """Définit/Ajoute au tableau des arcs d'une instance de la classe Graphe un ensemble de nouveaux arcs. Paramètres ---------- nouveauxArcs : list - Une liste d'instances de la classe Arcs. Tests ----- >>> g1 = Graphe('graphe1') >>> print(g1) Graphe(nom=graphe1, noeuds=[], arcs=[]) >>> n1 = Noeud('n1') >>> n2 = Noeud('n2') >>> n3 = Noeud('n3') >>> nouveauxNoeuds = [n1, n2, n3] >>> g1.setNoeuds(nouveauxNoeuds) >>> print(g1) Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=[]) >>> a1 = Arc('n1n2_0', n1, n2, 1) >>> a2 = Arc('n1n2_1', n1, n2, 2) >>> a3 = Arc('n1n3_0', n1, n3, 3) >>> a4 = Arc('n2n3_0', n2, n3, 4) >>> nouveauxArcs = [a1, a2, a3, a4] >>> g1.setArcs(nouveauxArcs) >>> print(g1) Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=['n1n2_0', 'n1n2_1', 'n1n3_0', 'n2n3_0']) """ # noqa : E501 pass # Un booléen pour tester si tout est OK allOk = True for nouvelArc in nouveauxArcs: assert nouvelArc not in self.arcs, "On ne peut ajouter un arc déjà présent." # noqa : E501 if nouvelArc in self.getArcs(): allOk = False raise ValueError("On ne peut ajouter un arc déjà présent.") assert nouvelArc.noeudDebut in self.noeuds or nouvelArc.noeudFin in self.noeuds, "NouvelArc doit lier deux noeuds du graphe." # noqa : E501 if (nouvelArc.getNoeudDebut() not in self.getNoeuds() or nouvelArc.getNoeudFin() not in self.getNoeuds()): allOk = False raise ValueError("NouvelArc doit lier deux noeuds du graphe.") if allOk: for arc in nouveauxArcs: self.addArc(arc) # ================================================================ # =========== OUTILS ============================================= # ================================================================ def addNoeud(self, noeudCandidat: Noeud) -> None: """Ajoute une instance de la classe Noeud au graphe. Paramètres ---------- noeudCandidat : Noeud - Un noeud à ajouter au graphe. Tests ----- >>> g1 = Graphe('graphe1') >>> print(g1) Graphe(nom=graphe1, noeuds=[], arcs=[]) >>> n1 = Noeud('n1') >>> g1.addNoeud(n1) >>> print(g1) Graphe(nom=graphe1, noeuds=['n1'], arcs=[]) """ pass assert isinstance(noeudCandidat, Noeud), "noeudCandidat doit être une instance de la classe Noeud." # noqa : E501 assert noeudCandidat not in self.noeuds, "Pas de doublon pour les noeuds d'une instance de Graphe." # noqa : E501 nomsDesNoeudsDuGraphe = [] for noeud in self.noeuds: nomsDesNoeudsDuGraphe.append(noeud.nom) assert noeudCandidat.nom not in nomsDesNoeudsDuGraphe, "Deux noeuds d'une instance de Graphe ne peuvent avoir le même nom." # noqa : E501 # Un booléen pour tester si tout est OK allOk = True # Ne pas utiliser les assertions pour valider les données # car elles peuvent être désactivées. # On lève donc une erreur en cas de souci if self.isNoeudInGraphe(noeudCandidat): allOk = False raise ValueError('Pas de doublon pour les noeuds d\'une instance de Graphe.') # noqa : E501 if noeudCandidat.nom in nomsDesNoeudsDuGraphe: allOk = False raise ValueError('Deux noeuds d\'une instance de Graphe ne peuvent avoir le même nom.') # noqa : E501 if allOk: self.noeuds.append(noeudCandidat) def isNoeudInGraphe(self, noeudCandidat: Noeud) -> bool: """Renvoie un booléen selon que l'instance de Noeud est dans le graphe ou non. Paramètres ---------- noeudCandidat : Noeud - Une instance de la classe Noeud dont on veut savoir si elle appartient au graphe. Tests ----- >>> g1 = Graphe('graphe1') >>> n1,n2 = Noeud('n1'),Noeud('n2') >>> g1.addNoeud(n1) >>> g1.isNoeudInGraphe(n1) True >>> g1.isNoeudInGraphe(n2) False """ # noqa : E501 pass assert isinstance(noeudCandidat, Noeud), "Le noeudCandidat doit être un instance de la classe Noeud." # noqa : E501 isNoeudInGraphe = False if noeudCandidat in self.getNoeuds(): isNoeudInGraphe = True return isNoeudInGraphe def hasNoeud(self) -> bool: """Renvoie un booléen selon que la propriété noeuds de l'instance de la classe Graphe est une liste vide ou non. Tests ----- >>> g1 = Graphe('graphe1') >>> n1 = Noeud('n1') >>> g1.hasNoeud() False >>> g1.addNoeud(n1) >>> g1.hasNoeud() True """ # noqa : E501 pass hasNoeud = False if self.getNoeuds(): hasNoeud = True return hasNoeud def addArc(self, arcCandidat: Arc) -> None: """Ajoute une instance de la classe Arc au graphe. Paramètres ---------- arcCandidat : Arc - Un arc à ajouter au graphe. Tests ----- >>> g1 = Graphe('graphe1') >>> print(g1) Graphe(nom=graphe1, noeuds=[], arcs=[]) >>> n1 = Noeud('n1') >>> n2 = Noeud('n2') >>> g1.addNoeud(n1) >>> g1.addNoeud(n2) >>> print(g1) Graphe(nom=graphe1, noeuds=['n1', 'n2'], arcs=[]) >>> arc = Arc('n1n2', n1, n2, 7) >>> g1.addArc(arc) >>> print(g1) Graphe(nom=graphe1, noeuds=['n1', 'n2'], arcs=['n1n2']) """ pass assert isinstance(arcCandidat, Arc), "arc doit être une instance de la classe Arc." # noqa : E501 assert arcCandidat not in self.arcs, "Pas de doublon pour les arcs d'une instance de Graphe." # noqa : E501 nomsDesArcsDuGraphe = [] for arc in self.getArcs(): nomsDesArcsDuGraphe.append(arc.nom) assert arcCandidat.nom not in nomsDesArcsDuGraphe,"Deux arcs d'une instance de Graphe ne peuvent avoir le même nom." # noqa : E501 # Un booléen pour tester si tout est OK allOk = True # Ne pas utiliser les assertions pour valider les données # car elles peuvent être désactivées. # On lève donc une erreur en cas de souci if arcCandidat in self.getArcs(): allOk = False raise ValueError('Pas de doublon pour les arcs d\'une instance de Graphe.') # noqa : E501 if arcCandidat.nom in nomsDesArcsDuGraphe: allOk = False raise ValueError('Deux arcs d\'une instance de Graphe ne peuvent avoir le même nom.') # noqa : E501 if allOk: self.arcs.append(arcCandidat) def addArcs(self, arcsToAdd: list) -> None: """Ajoute une liste d'instances de la classe Arc au graphe. Paramètres ---------- arcsToAdd : list - Une liste d'arcs à ajouter au graphe. Tests ----- >>> g = Graphe('graphe') >>> print(g) Graphe(nom=graphe, noeuds=[], arcs=[]) >>> A,B = Noeud('A'), Noeud('B') >>> g.setNoeuds([A,B]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B'], arcs=[]) >>> AB1 = Arc('A->B(1)', A, B, 7) >>> g.addArc(AB1) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B'], arcs=['A->B(1)']) >>> AB2,AB3 = Arc('A->B(2)', A, B, 7),Arc('A->B(3)', A, B, 7) >>> g.addArcs([AB2,AB3]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B'], arcs=['A->B(1)', 'A->B(2)', 'A->B(3)']) """ # noqa : E501 pass for arc in arcsToAdd: assert isinstance(arc, Arc), "arc doit être une instance de la classe Arc." # noqa : E501 for arc in arcsToAdd: self.addArc(arc) def removeArc(self, arcCandidat: Arc) -> None: """Supprime une instance de la classe Arc du graphe. Paramètres ---------- arcCandidat : Arc - Un arc à supprimer du graphe. Tests ----- >>> g1 = Graphe('graphe1') >>> print(g1) Graphe(nom=graphe1, noeuds=[], arcs=[]) >>> n1,n2,n3 = Noeud('n1'),Noeud('n2'),Noeud('n3') >>> g1.setNoeuds([n1,n2,n3]) >>> print(g1) Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=[]) >>> a1 = Arc('n1n2_1', n1, n2, 1) >>> a2 = Arc('n1n2_2', n1, n2, 2) >>> a3 = Arc('n1n3', n1, n3, 3) >>> g1.setArcs([a1,a2,a3]) >>> print(g1) Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=['n1n2_1', 'n1n2_2', 'n1n3']) >>> g1.removeArc(a1) >>> print(g1) Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=['n1n2_2', 'n1n3']) >>> for n in g1.getNoeuds(): ... print(n) ... Noeud(nom=n1, voisins=['n2', 'n3'], nbArcs=2, capital=0) Noeud(nom=n2, voisins=['n1'], nbArcs=1, capital=0) Noeud(nom=n3, voisins=['n1'], nbArcs=1, capital=0) >>> g1.removeArc(a3) >>> for n in g1.getNoeuds(): ... print(n) ... Noeud(nom=n1, voisins=['n2'], nbArcs=1, capital=0) Noeud(nom=n2, voisins=['n1'], nbArcs=1, capital=0) Noeud(nom=n3, voisins=[], nbArcs=0, capital=0) """ # noqa : E501 pass assert isinstance(arcCandidat, Arc), "arc doit être une instance de la classe Arc." # noqa : E501 assert self.isArcInGraphe(arcCandidat), "L'arc à supprimer doit faire partie des arcs du graphe." # noqa : E501 # Un booléen pour tester si tout est OK allOk = True # Ne pas utiliser les assertions pour valider les données # car elles peuvent être désactivées. # On lève donc une erreur en cas de souci if not self.isArcInGraphe(arcCandidat): allOk = False raise ValueError("L'arc à supprimer doit faire partie des arcs du graphe.") # noqa : E501 if allOk: self.getArcs().remove(arcCandidat) # Après suppression de l'arcCandidat, # Si il n'y a plus d'arcs entre eux # On met à jour le tableau des voisins des Noeuds # Sinon, on gère uniquement les nombres d'arcs des Noeuds ! noeudDebut = arcCandidat.getNoeudDebut() noeudFin = arcCandidat.getNoeudFin() if ((noeudDebut not in self.getDebiteurs(noeudFin)) and (noeudDebut not in self.getCreanciers(noeudFin))): noeudDebut.removeVoisin(noeudFin) else: noeudDebut.setNbArcs(noeudDebut.getNbArcs() - 1) noeudFin.setNbArcs(noeudFin.getNbArcs() - 1) def isArcInGraphe(self, arcCandidat: Arc) -> bool: """Renvoie un booléen selon que l'instance de Arc est dans le graphe ou non. Paramètres ---------- arcCandidat : Arc - Une instance de la classe Arc dont on veut savoir si elle appartient au graphe. Tests ----- >>> g1 = Graphe('graphe1') >>> n1,n2,n3 = Noeud('n1'),Noeud('n2'),Noeud('n3') >>> g1.setNoeuds([n1,n2,n3]) >>> a1,a2 = Arc('n1n2',n1,n2,1), Arc('n1n3',n1,n3,2) >>> g1.addArc(a1) >>> g1.isArcInGraphe(a1) True >>> g1.isArcInGraphe(a2) False """ # noqa : E501 pass assert isinstance(arcCandidat, Arc), "arcCandidat doit être un instance de la classe Arc." # noqa : E501 isArcInGraphe = False if arcCandidat in self.getArcs(): isArcInGraphe = True return isArcInGraphe def hasArc(self) -> bool: """Renvoie un booléen selon que la propriété arcs de l'instance de la classe Graphe est une liste vide ou non. Tests ----- >>> g1 = Graphe('graphe1') >>> n1,n2 = Noeud('n1'),Noeud('n2') >>> arc = Arc('n1n2',n1,n2,1) >>> g1.hasArc() False >>> g1.addNoeud(n1) >>> g1.addNoeud(n2) >>> g1.addArc(arc) >>> g1.hasArc() True """ # noqa : E501 pass hasArc = False if self.getArcs(): hasArc = True return hasArc def getEntreprises(self) -> list: """Récupère dans une liste tous les noeuds du graphe, qui sont les entrepises de la situation problème étudiée. Tests ----- >>> g1 = Graphe('graphe1') >>> g1.getEntreprises() [] >>> n1,n2,n3 = Noeud('n1'),Noeud('n2'),Noeud('n3') >>> g1.setNoeuds([n1,n2,n3]) >>> # On affiche uniquement les noms des noeuds pour le doctest >>> for noeud in g1.getEntreprises(): ... print(noeud.nom) ... n1 n2 n3 """ # noqa : E501 pass return self.getNoeuds() def getDebiteurs(self, noeudCandidat: Noeud) -> list: """Récupére dans une liste, tous les noeuds débiteurs du noeudCandidat. Paramètres ---------- noeudCandidat : Noeud - Une instance de la classe Noeud dont on veut les débiteurs. Tests ----- >>> g1 = Graphe('graphe1') >>> n1,n2,n3,n4 = Noeud('n1'),Noeud('n2'),Noeud('n3'),Noeud('n4') >>> g1.setNoeuds([n1,n2,n3,n4]) >>> a1,a2,a3,a4 = Arc('n2n1',n2,n1,1),Arc('n3n1',n3,n1,2),Arc('n4n1',n4,n1,3),Arc('n1n4',n1,n4,4) >>> g1.setArcs([a1,a2,a3,a4]) >>> # On affiche uniquement les noms des noeuds débiteurs pour le doctest >>> for noeud in g1.getDebiteurs(n1): ... print(noeud.nom) ... n2 n3 n4 >>> for noeud in g1.getDebiteurs(n2): ... print(noeud.nom) ... >>> for noeud in g1.getDebiteurs(n4): ... print(noeud.nom) ... n1 """ # noqa : E501 pass assert self.isNoeudInGraphe(noeudCandidat), "noeudCandidat doit être un noeud du graphe." # noqa : E501 # Ne pas utiliser les assertions pour valider les données # car elles peuvent être désactivées. # On lève donc une erreur en cas de souci if not self.isNoeudInGraphe(noeudCandidat): raise ValueError("noeudCandidat doit être un noeud du graphe.") else: noeudsDebiteurs = [] for arc in self.getArcs(): if arc.getNoeudFin() == noeudCandidat: noeudsDebiteurs.append(arc.getNoeudDebut()) return noeudsDebiteurs def getCreanciers(self, noeudCandidat: Noeud) -> list: """Récupére dans une liste, tous les noeuds créanciers du noeudCandidat. Paramètres ---------- noeudCandidat : Noeud - Une instance de la classe Noeud dont on veut les créanciers. Tests ----- >>> g1 = Graphe('graphe1') >>> n1,n2,n3,n4,n5 = Noeud('n1'),Noeud('n2'),Noeud('n3'),Noeud('n4'),Noeud('n5') >>> g1.setNoeuds([n1,n2,n3,n4,n5]) >>> a1,a2,a3,a4 = Arc('n2n1',n2,n1,1),Arc('n3n1',n3,n1,2),Arc('n4n1',n4,n1,3),Arc('n1n4',n1,n4,4) >>> a5,a6 = Arc('n1n3',n1,n3,5),Arc('n1n5',n1,n5,6) >>> g1.setArcs([a1,a2,a3,a4,a5,a6]) >>> # On affiche uniquement les noms des noeuds créanciers pour le doctest >>> for noeud in g1.getCreanciers(n1): ... print(noeud.nom) ... n4 n3 n5 >>> for noeud in g1.getCreanciers(n2): ... print(noeud.nom) ... n1 >>> for noeud in g1.getCreanciers(n5): ... print(noeud.nom) ... """ # noqa : E501 pass assert self.isNoeudInGraphe(noeudCandidat), "noeudCandidat doit être un noeud du graphe." # noqa : E501 # Ne pas utiliser les assertions pour valider les données # car elles peuvent être désactivées. # On lève donc une erreur en cas de souci if not self.isNoeudInGraphe(noeudCandidat): raise ValueError("noeudCandidat doit être un noeud du graphe.") else: noeudsCreanciers = [] for arc in self.getArcs(): if arc.getNoeudDebut() == noeudCandidat: noeudsCreanciers.append(arc.getNoeudFin()) return noeudsCreanciers def positionNette( self, noeudCandidat: Noeud, algo1NomEntreprise=None, algo1Don=0 ) -> int: """Retourne la balance entre dettes et créances d'une instance de la classe Noeud. Paramètres ---------- - noeudCandidat : Noeud - Une instance de la classe Noeud dont on veut les créanciers. - algo1NomEntreprise : str - Le nom de l'entreprise choisie pour le don dans l'algorithme 1 - méthode heuristique - algo1Don : int - Une valeur correspondant au don dans l'algorithme 1 méthode heuristique Tests ----- >>> g1 = Graphe('graphe1') >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> # arcs partant de A >>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3) >>> # arcs arrivant sur A >>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1) >>> # arc entre B et C >>> BC = Arc('BC',B,C,5) >>> g1.setNoeuds([A,B,C]) >>> g1.setArcs([AB1,AB2,AC,CA1,CA2,BC]) >>> g1.positionNette(A) -1 >>> g1.positionNette(A,'A',5) -6 >>> g1.positionNette(B) 2 >>> g1.positionNette(B,'A',5) 2 >>> g1.positionNette(C) -1 """ # noqa : E501 pass assert self.isNoeudInGraphe(noeudCandidat), "noeudCandidat doit être un noeud du graphe." # noqa : E501 # Ne pas utiliser les assertions pour valider les données # car elles peuvent être désactivées. # On lève donc une erreur en cas de souci if not self.isNoeudInGraphe(noeudCandidat): raise ValueError("noeudCandidat doit être un noeud du graphe.") else: # Total des sommes dues aux créanciers du noeudCandidat detteTotaleDuNoeud = 0 # Total des sommes dues par les débiteurs du noeudCandidat creanceTotaleDuNoeud = 0 debiteurs = self.getDebiteurs(noeudCandidat) creanciers = self.getCreanciers(noeudCandidat) for arc in self.getArcs(): noeudDebut = arc.getNoeudDebut() noeudFin = arc.getNoeudFin() if noeudDebut == noeudCandidat and noeudFin in creanciers: detteTotaleDuNoeud += arc.poids if noeudDebut in debiteurs and noeudFin == noeudCandidat: creanceTotaleDuNoeud += arc.poids if noeudCandidat.getNom() == algo1NomEntreprise: positionNetteDuNoeud = (noeudCandidat.getCapital() + creanceTotaleDuNoeud - detteTotaleDuNoeud - algo1Don) else: positionNetteDuNoeud = (noeudCandidat.getCapital() + creanceTotaleDuNoeud - detteTotaleDuNoeud) return positionNetteDuNoeud def detteGlobale(self) -> int: """Retourne la dette globale du graphe, c'est à dire la somme des dettes des entreprises. Tests ----- >>> g1 = Graphe('graphe1') >>> E,D,F = Noeud('E'),Noeud('D'),Noeud('F') >>> ED,DF,FE = Arc('ED',E,D,10),Arc('DF',D,F,7),Arc('FE',F,E,2) >>> g1.setNoeuds([E,D,F]) >>> g1.setArcs([ED,DF,FE]) >>> g1.detteGlobale() 19 >>> g2 = Graphe('graphe2') >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> # arcs partant de A >>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3) >>> # arcs arrivant sur A >>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1) >>> # arc entre B et C >>> BC = Arc('BC',B,C,5) >>> g2.setNoeuds([A,B,C]) >>> g2.setArcs([AB1,AB2,AC,CA1,CA2,BC]) >>> g2.detteGlobale() 24 """ # noqa : E501 pass assert len(self.getNoeuds()) >= 2, "Le graphe doit avoir au moins deux noeuds." # noqa : E501 # Ne pas utiliser les assertions pour valider les données # car elles peuvent être désactivées. # On lève donc une erreur en cas de souci allOk = True if len(self.getNoeuds()) < 2: allOk = False raise ValueError("Le graphe doit avoir au moins deux noeuds.") if allOk: detteGlobale = 0 for arc in self.getArcs(): detteGlobale += arc.poids return detteGlobale def simplification(self) -> None: """Modifie l'instance du graphe en remplaçant pour chaque couple d'entreprises A,B, l'ensemble des dettes de A vers B par une seule dette égale à la somme des dettes. Tests ----- >>> g = Graphe('graphe') >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> AB2 = Arc('AB2',A,B,2) >>> BC7,BC4 = Arc('BC7',B,C,7),Arc('BC4',B,C,4) >>> CA2,CA5,CA9 = Arc('CA2',C,A,2),Arc('CA5',C,A,5),Arc('CA9',C,A,9) >>> g.setNoeuds([A,B,C]) >>> g.setArcs([AB2,BC7,BC4,CA2,CA5,CA9]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB2', 'BC7', 'BC4', 'CA2', 'CA5', 'CA9']) >>> g.simplification() >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-2->B', 'B-11->C', 'C-16->A']) >>> # Test sur le graphe du document de cours >>> g1 = Graphe('graphe1') >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> # arcs partant de A >>> AB,ABbis,AC = Arc('AB',A,B,5),Arc('ABbis',A,B,2),Arc('AC',A,C,3) >>> # arcs arrivant sur A >>> CA,CAbis = Arc('CA',C,A,8),Arc('CAbis',C,A,1) >>> # arc entre B et C >>> BC = Arc('BC',B,C,5) >>> g1.setNoeuds([A,B,C]) >>> g1.setArcs([AB,ABbis,AC,CA,CAbis,BC]) >>> print(g1) Graphe(nom=graphe1, noeuds=['A', 'B', 'C'], arcs=['AB', 'ABbis', 'AC', 'CA', 'CAbis', 'BC']) >>> for noeud in g1.getNoeuds(): ... print(noeud) ... Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0) Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=0) Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0) >>> g1.simplification() >>> print(g1) Graphe(nom=graphe1, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A']) >>> for noeud in g1.getNoeuds(): ... print(noeud) ... Noeud(nom=A, voisins=['B', 'C'], nbArcs=3, capital=0) Noeud(nom=B, voisins=['A', 'C'], nbArcs=2, capital=0) Noeud(nom=C, voisins=['A', 'B'], nbArcs=3, capital=0) """ # noqa : E501 pass # Si A et B sont voisins alors # soit A a des dettes envers B, soit B a des dettes envers A. newArcs = [] for noeud in self.getNoeuds(): for voisin in noeud.getVoisins(): detteTotale = 0 for arc in self.getArcs(): if (noeud == arc.getNoeudDebut() and voisin == arc.getNoeudFin()): # On cumule la dette detteTotale += arc.getPoids() if detteTotale != 0: # On ajoute un nouvel arc avec la dette totale # au tableau des nouveaux arcs nouveauNom = noeud.getNom()+'-'+str(detteTotale)+'->'+voisin.getNom() # noqa : E501 newArc = Arc(nouveauNom, noeud, voisin, detteTotale) newArcs.append(newArc) # Le graphe garde les mêmes noeuds # Mais on remplace tous les arcs # On commence par supprimer tous les arcs existants index = len(self.getArcs())-1 while index != -1: self.removeArc(self.getArcs()[index]) index -= 1 # On ajoute les nouveaux arcs self.setArcs(newArcs) # Il faut aussi redefinir tous les voisins for arc in newArcs: if not arc.getNoeudDebut().isVoisin(arc.getNoeudFin()): arc.getNoeudDebut().addVoisin(arc.getNoeudFin()) # Il faut aussi redefinir le nombre d'arcs de chaque noeud # On commence par mettre le nombre d'arcs à 0 for noeud in self.getNoeuds(): noeud.setNbArcs(0) # On met à jour le nombre d'arcs de chaque noeud for arc in newArcs: if noeud == arc.getNoeudDebut(): noeud.setNbArcs(noeud.getNbArcs()+1) if noeud == arc.getNoeudFin(): noeud.setNbArcs(noeud.getNbArcs()+1) def getArcsDirects(self) -> list: """Renvoie la liste des arcs directs du graphe. Tests ----- >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3) >>> g = Graphe('graphe') >>> g.setNoeuds([A,B,C]) >>> g.setArcs([AB5,AB2,AC3]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C']) >>> # Pour les tests, on ajoute les arcs inverses à la main >>> # car la méthode addArcsInversesNuls() simplifie le graphe s'il ne l'est pas >>> BA5i,BA2i,CA3i = Arc('B-5->A(i)',B,A,0),Arc('B-2->A(i)',B,A,0),Arc('C-3->A(i)',C,A,0) >>> arcsInverses = [BA5i,BA2i,CA3i] >>> for arc in arcsInverses: ... arc.setEstInverse(True) >>> g.addArcs([BA5i,BA2i,CA3i]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->A(i)', 'B-2->A(i)', 'C-3->A(i)']) >>> for arc in g.getArcsDirects(): ... print(arc.getNom()) ... A-5->B A-2->B A-3->C >>> g.addArcsInversesNuls() >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-0->A', 'C-0->A']) >>> for arc in g.getArcsDirects(): ... print(arc.getNom()) ... A-7->B A-3->C """ # noqa : E501 pass arcsDirects = [] for arc in self.getArcs(): if not arc.getEstInverse(): arcsDirects.append(arc) return arcsDirects def getArcsInverses(self) -> list: """Renvoie la liste des arcs inverses du graphe. Tests ----- >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3) >>> g = Graphe('graphe') >>> g.setNoeuds([A,B,C]) >>> g.setArcs([AB5,AB2,AC3]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C']) >>> # Pour les tests, on ajoute les arcs inverses à la main >>> # car la méthode addArcsInversesNuls() simplifie le graphe s'il ne l'est pas >>> BA5i,BA2i,CA3i = Arc('B-5->A(i)',B,A,0,True),Arc('B-2->A(i)',B,A,0,True),Arc('C-3->A(i)',C,A,0,True) >>> arcsInverses = [BA5i,BA2i,CA3i] >>> g.addArcs([BA5i,BA2i,CA3i]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->A(i)', 'B-2->A(i)', 'C-3->A(i)']) >>> for arc in g.getArcsInverses(): ... print(arc.getNom()) B-5->A(i) B-2->A(i) C-3->A(i) >>> g.addArcsInversesNuls() >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-0->A', 'C-0->A']) >>> for arc in g.getArcsInverses(): ... print(arc.getNom()) ... B-0->A C-0->A """ # noqa : E501 pass arcsInverses = [] for arc in self.getArcs(): if arc.getEstInverse(): arcsInverses.append(arc) return arcsInverses def estSimplifie(self) -> bool: """Retourne un booléen selon que le graphe est simplifié ou non. Tests ----- >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3) >>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1) >>> g = Graphe('graphe') >>> g.setNoeuds([A,B,C]) >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A']) >>> g.estSimplifie() False >>> g1 = copy.deepcopy(g) >>> g1.simplification() >>> print(g1) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A']) >>> g1.estSimplifie() True >>> g2 = Graphe('graphe2') >>> g2.setNoeuds([A,B]) >>> AB1,BA1 = Arc('A-1->B(1)',A,B,1),Arc('B-1->A(1)',B,A,1) >>> g2.setArcs([AB1,BA1]) >>> print(g2) Graphe(nom=graphe2, noeuds=['A', 'B'], arcs=['A-1->B(1)', 'B-1->A(1)']) >>> g2.addArcsInversesNuls() >>> print(g2) Graphe(nom=graphe2, noeuds=['A', 'B'], arcs=['A-1->B(1)', 'B-1->A(1)', 'B-0->A', 'A-0->B']) >>> # la méthode estSimplifie() ne doit tenir compte que des arcs directs >>> g2.estSimplifie() True """ # noqa : E501 pass arcsDirects = self.getArcsDirects() estSimplifie = True for i in range(len(arcsDirects)): k = 0 for j in range(len(arcsDirects)): if (arcsDirects[i].getNoeudDebut() == arcsDirects[j].getNoeudDebut() and # noqa : E501 arcsDirects[i].getNoeudFin() == arcsDirects[j].getNoeudFin()): # noqa : E501 k += 1 if k > 1: estSimplifie = False return estSimplifie def parcoursEnProfondeurNoeuds( self, noeudDepart: Noeud, dejaVisites=None ) -> list: """Renvoie les noeuds du graphe accessibles depuis noeudDepart. Paramètres ---------- - noeudDepart : Noeud - Le noeud de départ de l'exploration. - dejaVisites : list - Une liste avec les noeuds déjà visités. Remarque -------- - Fonction recursive Tests ----- >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E') >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1) >>> BA = Arc('B->A',B,A,1) >>> CB = Arc('C->B',C,B,1) >>> DE = Arc('D->E',D,E,2) >>> EA = Arc('E->A',E,A,3) >>> g = Graphe('graphe') >>> g.setNoeuds([A,B,C,D,E]) >>> g.setArcs([AB,AC,AD,CB,DE,EA]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'A->D', 'C->B', 'D->E', 'E->A']) >>> for n in g.parcoursEnProfondeurNoeuds(A,[]): ... print(n.getNom()) ... A B C D E >>> g1 = Graphe('graphe1') >>> g1.setNoeuds([A,B,C,D,E]) >>> g1.setArcs([AB,AC,BA,CB,DE,EA]) >>> print(g1) Graphe(nom=graphe1, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'B->A', 'C->B', 'D->E', 'E->A']) >>> for n in g1.parcoursEnProfondeurNoeuds(A,[]): ... print(n.getNom()) ... A B C """ # noqa : E501 pass assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud." # noqa : E501 assert isinstance(dejaVisites,list) or dejaVisites is None,"dejaVisites vaut None ou est une liste." # noqa : E501 if dejaVisites is None: dejaVisites = [] if noeudDepart not in dejaVisites: dejaVisites.append(noeudDepart) for voisin in self.getCreanciers(noeudDepart): self.parcoursEnProfondeurNoeuds(voisin, dejaVisites) return dejaVisites def getArcsCreanciers(self, noeudDepart: Noeud) -> list: """Renvoie une liste de tous les arcs partants de noeudDepart. Paramètres ---------- noeudDepart : Noeud - Le noeud dont on veut la liste des créanciers. Tests ----- >>> g = Graphe('graphe') >>> A,B,D,E = Noeud('A'),Noeud('B'),Noeud('D'),Noeud('E') >>> AB1,AB2,AB3 = Arc('A->B(1)',A,B,1),Arc('A->B(2)',A,B,1),Arc('A->B(3)',A,B,1) >>> DE = Arc('D->E',D,E,1) >>> EA = Arc('E->A',E,A,1) >>> g.setNoeuds([A,B,D,E]) >>> g.setArcs([AB1,AB2,AB3,DE,EA]) >>> for a in g.getArcsCreanciers(A): ... print(a.getNom()) ... A->B(1) A->B(2) A->B(3) >>> for a in g.getArcsCreanciers(D): ... print(a.getNom()) ... D->E """ # noqa : E501 pass assert self.isNoeudInGraphe(noeudDepart), "noeudDepart doit être un noeud du graphe." # noqa : E501 # Ne pas utiliser les assertions pour valider les données # car elles peuvent être désactivées. # On lève donc une erreur en cas de souci if not self.isNoeudInGraphe(noeudDepart): raise ValueError("noeudDepart doit être un noeud du graphe.") else: arcsCreanciers = [] for arc in self.getArcs(): if arc.getNoeudDebut() == noeudDepart: arcsCreanciers.append(arc) return arcsCreanciers def parcoursEnProfondeurArcs( self, noeudDepart: Noeud, dejaVisites=None ) -> list: """Renvoie les arcs du graphe accessibles depuis noeudDepart. Paramètres ---------- - noeudDepart : Noeud - Le noeud dont on veut les arcs accessibles. - dejaVisites : list - Les arcs déjà visités. Remarque -------- - Fonction recursive Tests ----- >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E') >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1) >>> BA = Arc('B->A',B,A,1) >>> CB = Arc('C->B',C,B,1) >>> DE = Arc('D->E',D,E,2) >>> EA = Arc('E->A',E,A,3) >>> g = Graphe('graphe') >>> g.setNoeuds([A,B,C,D,E]) >>> g.setArcs([AB,AC,AD,CB,DE,EA]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'A->D', 'C->B', 'D->E', 'E->A']) >>> for n in g.parcoursEnProfondeurArcs(A,[]): ... print(n.getNom()) ... A->B C->B D->E E->A A->C A->D >>> g1 = Graphe('graphe1') >>> g1.setNoeuds([A,B,C,D,E]) >>> g1.setArcs([AB,AC,BA,CB,DE,EA]) >>> print(g1) Graphe(nom=graphe1, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'B->A', 'C->B', 'D->E', 'E->A']) >>> for n in g1.parcoursEnProfondeurArcs(A,[]): ... print(n.getNom()) ... A->B B->A A->C C->B """ # noqa : E501 pass assert isinstance(noeudDepart, Noeud), "arcDepart doit être un instance de la classe Arc." # noqa : E501 assert isinstance(dejaVisites,list) or dejaVisites is None,"dejaVisites vaut None ou est une liste." # noqa : E501 if dejaVisites is None: dejaVisites = [] for arc in self.getArcsCreanciers(noeudDepart): if arc not in dejaVisites: dejaVisites.append(arc) for voisin in self.getCreanciers(noeudDepart): self.parcoursEnProfondeurArcs(voisin, dejaVisites) return dejaVisites def hasCycleFromNoeud(self, noeudCandidat: Noeud) -> bool: """Renvoie un booléen selon que le graphe contient un cycle à partir du noeudCandidat ou non. Paramètres ---------- noeudCandidat : Noeud - Un noeud du graphe. Tests ----- >>> g = Graphe('graphe') >>> A,B,C,D,E = Noeud('0'),Noeud('1'),Noeud('2'),Noeud('3'),Noeud('4') >>> AB,AC,AD = Arc('0->1',A,B,1),Arc('0->2',A,C,1),Arc('0->3',A,D,1) >>> CB = Arc('2->1',C,B,1) >>> DE = Arc('3->4',D,E,1) >>> EA = Arc('4->0',E,A,1) >>> g.setNoeuds([A,B,C,D,E]) >>> g.setArcs([AB,AC,AD,CB,DE,EA]) >>> print(g) Graphe(nom=graphe, noeuds=['0', '1', '2', '3', '4'], arcs=['0->1', '0->2', '0->3', '2->1', '3->4', '4->0']) >>> # Ce graphe contient un cycle à partir des noeuds A,D ou E : A->D->E->A >>> print(g.hasCycleFromNoeud(A)) True >>> print(g.hasCycleFromNoeud(B)) False >>> print(g.hasCycleFromNoeud(C)) False >>> print(g.hasCycleFromNoeud(D)) True >>> print(g.hasCycleFromNoeud(E)) True """ # noqa : E501 pass assert isinstance(noeudCandidat, Noeud), "Le noeudCandidat doit être un instance de la classe Noeud." # noqa : E501 # Il suffit que l'un des creanciers du noeudCandidat # puisse accéder à nouveau à noeudCandidat boolDeSortie = False index = 1 noeudsAccessibles = self.parcoursEnProfondeurNoeuds(noeudCandidat) for index in range(1, len(noeudsAccessibles)): if noeudCandidat in self.parcoursEnProfondeurNoeuds(noeudsAccessibles[index]): # noqa : E501 boolDeSortie = True return boolDeSortie def hasCycle(self) -> None: """Renvoie un booléen selon que le graphe a encore au moins un cycle. Tests ----- >>> g = Graphe('graphe') >>> A,B,C,D,E = Noeud('0'),Noeud('1'),Noeud('2'),Noeud('3'),Noeud('4') >>> AB,AC,AD = Arc('0->1',A,B,1),Arc('0->2',A,C,1),Arc('0->3',A,D,1) >>> CB = Arc('2->1',C,B,1) >>> DE = Arc('3->4',D,E,1) >>> EA = Arc('4->0',E,A,1) >>> g.setNoeuds([A,B,C,D,E]) >>> g.setArcs([AB,AC,AD,CB,DE,EA]) >>> print(g) Graphe(nom=graphe, noeuds=['0', '1', '2', '3', '4'], arcs=['0->1', '0->2', '0->3', '2->1', '3->4', '4->0']) >>> # Ce graphe contient un cycle à partir des noeuds A,D ou E : A->D->E->A >>> g.hasCycle() True >>> g1 = Graphe('graphe1') >>> g1.setNoeuds([A,B,C,D,E]) >>> g1.setArcs([AB,AC,AD,CB,EA]) >>> print(g1) Graphe(nom=graphe1, noeuds=['0', '1', '2', '3', '4'], arcs=['0->1', '0->2', '0->3', '2->1', '4->0']) >>> # Ce graphe ne contient pas de cycle >>> g1.hasCycle() False """ # noqa : E501 pass boolDeSortie = False index = 0 noeudsDuGraphe = self.getNoeuds() while (not self.hasCycleFromNoeud(noeudsDuGraphe[index]) and index < len(noeudsDuGraphe)-1): index += 1 if index < len(noeudsDuGraphe)-1: boolDeSortie = True return boolDeSortie def getNodesWays( self, noeudDepart: Noeud, noeudArrivee: Noeud, way=None ) -> list: """Renvoie une liste de liste de noeuds contenant tous les chemins entre noeudDepart et noeudArrivee s'il en existe. Renvoie [] sinon. Paramètres ---------- - noeudDepart : Noeud - Le noeud de départ du chemin. - noeudArrivee : Noeud - Le noeud d'arrivée du chemin. - way : list - Un tableau contenant les noeuds permettant d'aller de noeudDepart à noeudArrivee Remarques --------- - Fonction recursive - Ne trouve pas les cycles - S'il existe plusieurs arcs entre deux noeuds, le chemin de noeud est identique et ressort donc plusieurs fois. Ce sera utile pour compter les chemins d'arcs distincts. Tests ----- >>> g = Graphe('graphe') >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E') >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1) >>> CB = Arc('C->B',C,B,1) >>> DE = Arc('D->E',D,E,1) >>> EA = Arc('E->A',E,A,1) >>> g.setNoeuds([A,B,C,D,E]) >>> g.setArcs([AB,AC,AD,CB,DE,EA]) >>> # Aucun chemin de B à C >>> for chemin in g.getNodesWays(B,C): ... print("====chemin====") ... for n in chemin: ... print(n) ... >>> # Deux chemins entre A et B >>> for chemin in g.getNodesWays(A,B): ... print("====chemin====") ... for n in chemin: ... print(n) ... ====chemin==== Noeud(nom=A, voisins=['B', 'C', 'D', 'E'], nbArcs=4, capital=0) Noeud(nom=B, voisins=['A', 'C'], nbArcs=2, capital=0) ====chemin==== Noeud(nom=A, voisins=['B', 'C', 'D', 'E'], nbArcs=4, capital=0) Noeud(nom=C, voisins=['A', 'B'], nbArcs=2, capital=0) Noeud(nom=B, voisins=['A', 'C'], nbArcs=2, capital=0) >>> g1 = Graphe('graphe1') >>> A1,B1,C1,D1 = Noeud('A1'),Noeud('B1'),Noeud('C1'),Noeud('D1') >>> A1B1_1,A1B1_2,A1B1_3 = Arc('A1->B1(1)',A1,B1,1),Arc('A1->B1(2)',A1,B1,1),Arc('A1->B1(3)',A1,B1,1) >>> B1C1_1,B1C1_2 = Arc('B1->C1(1)',B1,C1,1),Arc('B1->C1(2)',B1,C1,1) >>> C1A1 = Arc('C1->A1',C1,A1,1) >>> C1D1 = Arc('C1->D1',C1,D1,1) >>> g1.setNoeuds([A1,B1,C1,D1]) >>> g1.setArcs([A1B1_1,A1B1_2,A1B1_3,B1C1_1,B1C1_2,C1A1,C1D1]) >>> # Six chemins entre A1 et D1 >>> for chemin in g1.getNodesWays(A1,D1): ... print("====chemin====") ... for n in chemin: ... print(n) ... ====chemin==== Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0) Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0) Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0) Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0) ====chemin==== Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0) Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0) Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0) Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0) ====chemin==== Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0) Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0) Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0) Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0) ====chemin==== Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0) Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0) Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0) Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0) ====chemin==== Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0) Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0) Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0) Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0) ====chemin==== Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0) Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0) Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0) Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0) """ # noqa : E501 pass assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud." # noqa : E501 assert isinstance(noeudArrivee, Noeud), "Le noeudArrivee doit être un instance de la classe Noeud." # noqa : E501 assert isinstance(way, list) or way is None, "way vaut None ou est une liste." # noqa : E501 if way is None: way = [] way = way + [noeudDepart] if noeudDepart == noeudArrivee: return [way] ways = [] nonVisites = [] # Pour l'algorithme de Klein, notamment, # le fait de rechercher tous les chemins # fait exploser le temps de traitement sur des graphes de # taille réelle dès Gen_100 creanciersNoeudDepart = self.getCreanciers(noeudDepart) for noeud in creanciersNoeudDepart: if noeud not in way: nonVisites.append(noeud) for noeud in nonVisites: ways.extend(self.getNodesWays(noeud, noeudArrivee, way)) return ways def getArcsWays( self, noeudDepart: Noeud, noeudArrivee: Noeud, way=None ) -> list: """Renvoie une liste de tuples d'arcs contenant tous les chemins entre noeudDepart et noeudArrivee s'il en existe. Renvoie [] sinon. Paramètres ---------- - noeudDepart : Noeud - Le noeud de départ du chemin. - noeudArrivee : Noeud - Le noeud d'arrivée du chemin. - way : list - un tableau contenant les arcs permettant d'aller de noeudDepart à noeudArrivee. Remarques --------- - Ne trouve pas les cycles - S'il existe plusieurs arcs entre deux noeuds, le chemin de noeud est identique et ressort donc plusieurs fois. Ce sera utile pour compter les chemins d'arcs distincts. Tests ----- >>> g = Graphe('grapheLinéaire') >>> A,B,C,D = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D') >>> AB1,AB2,AB3 = Arc('A->B(1)',A,B,1),Arc('A->B(2)',A,B,1),Arc('A->B(3)',A,B,1) >>> BC1,BC2 = Arc('B->C(1)',B,C,1),Arc('B->C(2)',B,C,1) >>> CD1,CD2,CD3,CD4 = Arc('C->D(1)',C,D,1),Arc('C->D(2)',C,D,1),Arc('C->D(3)',C,D,1),Arc('C->D(4)',C,D,1) >>> g.setNoeuds([A,B,C,D]) >>> g.setArcs([AB1,AB2,AB3,BC1,BC2,CD1,CD2,CD3,CD4]) >>> # Il y a 24 chemins d'arcs possibles entre A et D >>> len(g.getArcsWays(A,D)) 24 >>> # Il y a 6 chemins d'arcs possibles entre A et C >>> # On les liste tous >>> len(g.getArcsWays(A,C)) 6 >>> for arcsWay in g.getArcsWays(A,C): ... print("=== Chemin d'arcs entre A et C ===") ... for arc in arcsWay: ... print(arc.getNom()) ... === Chemin d'arcs entre A et C === A->B(1) B->C(1) === Chemin d'arcs entre A et C === A->B(1) B->C(2) === Chemin d'arcs entre A et C === A->B(2) B->C(1) === Chemin d'arcs entre A et C === A->B(2) B->C(2) === Chemin d'arcs entre A et C === A->B(3) B->C(1) === Chemin d'arcs entre A et C === A->B(3) B->C(2) >>> # Tests sur un graphe non linéaire >>> # On ajoute deux arcs entre C et A >>> CA1,CA2 = Arc('C->A(1)',C,A,1),Arc('C->A(2)',C,A,1) >>> # On ajoute un arc entre A et C >>> AC1 = Arc('A->C(1)',A,C,1) >>> # Le graphe aura donc un cycle ABCA >>> # La méthode ne tolèrant pas de repasser par le noeud de départ >>> # Elle trouve bien 7 chemins entre A et C >>> g1 = Graphe('grapheNonLinéaire') >>> g1.setNoeuds([A,B,C,D]) >>> g1.setArcs([AB1,AB2,AB3,BC1,BC2,CD1,CD2,CD3,CD4,CA1,CA2,AC1]) >>> len(g1.getArcsWays(A,C)) 7 >>> for arcsWay in g1.getArcsWays(A,C): ... print("=== Chemin d'arcs entre A et C ===") ... for arc in arcsWay: ... print(arc.getNom()) ... === Chemin d'arcs entre A et C === A->B(1) B->C(1) === Chemin d'arcs entre A et C === A->B(1) B->C(2) === Chemin d'arcs entre A et C === A->B(2) B->C(1) === Chemin d'arcs entre A et C === A->B(2) B->C(2) === Chemin d'arcs entre A et C === A->B(3) B->C(1) === Chemin d'arcs entre A et C === A->B(3) B->C(2) === Chemin d'arcs entre A et C === A->C(1) """ # noqa : E501 pass assert isinstance(noeudDepart, Noeud), "noeudDepart doit être un instance de la classe Noeud." # noqa : E501 assert isinstance(noeudArrivee, Noeud), "Le noeudArrivee doit être un instance de la classe Noeud." # noqa : E501 assert isinstance(way, list) or way is None, "way vaut None ou est une liste." # noqa : E501 assert noeudDepart != noeudArrivee, "noeudDepart et nouedArrivee doivent être distincts" # noqa : E501 # Pour stocker tous les chemins d'arcs possibles arcsWays = [] # On liste tous les chemins de noeuds cheminsDeNoeudsPossibles = self.getNodesWays(noeudDepart, noeudArrivee) cheminsDeNoeudsSansDoublons = [] for chemin in cheminsDeNoeudsPossibles: if chemin not in cheminsDeNoeudsSansDoublons: cheminsDeNoeudsSansDoublons.append(chemin) # Pour chaque chemin de cheminsDeNoeudsSansDoublon # On va récupérer tous les arcs entre deux noeuds successifs # dans arcsWay for chemin in cheminsDeNoeudsSansDoublons: # le nombre d'espace inter Noeuds nbInterNoeuds = len(chemin)-1 # Pour stocker tous les arcs possibles entre deux noeuds du chemin arcsWay = [] for i in range(nbInterNoeuds): arcsWay.append(self.getArcsBetween(chemin[i], chemin[i + 1])) # On va stocker toutes les combinaisons d'arcs de ce chemin produitCartesienDesArcs = [] for combs in product(*arcsWay): produitCartesienDesArcs.append(combs) arcsWays.extend(produitCartesienDesArcs) return arcsWays def getAllNodesCyclesFromNoeud(self, noeudDepart: Noeud) -> list: """Renvoie tous les cycles partant du noeudDepart s'il en existe. Paramètres ---------- noeudDepart : Noeud - Le noeud de départ des cycles. Tests ----- >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E') >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1) >>> BA = Arc('B->A',B,A,1) >>> CB = Arc('C->B',C,B,1) >>> DE = Arc('D->E',D,E,2) >>> EA = Arc('E->A',E,A,3) >>> g = Graphe('graphe') >>> g.setNoeuds([A,B,C,D,E]) >>> g.setArcs([AB,AC,AD,CB,DE,EA]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'A->D', 'C->B', 'D->E', 'E->A']) >>> for cycle in g.getAllNodesCyclesFromNoeud(A): ... print("====cycle====") ... for n in cycle: ... print(n.getNom()) ... ====cycle==== A D E A >>> g1 = Graphe('graphe1') >>> g1.setNoeuds([A,B,C,D,E]) >>> g1.setArcs([AB,AC,BA,CB,DE,EA]) >>> print(g1) Graphe(nom=graphe1, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'B->A', 'C->B', 'D->E', 'E->A']) >>> for cycle in g1.getAllNodesCyclesFromNoeud(A): ... print("====cycle====") ... for n in cycle: ... print(n.getNom()) ... ====cycle==== A B A ====cycle==== A C B A >>> A1,B1,C1 = Noeud('A'),Noeud('B'),Noeud('C') >>> A1B1,A1B1bis,A1C1 = Arc('A-5->B',A1,B1,5),Arc('A-2->B',A1,B1,2),Arc('A-3->C',A1,C1,3) >>> B1C1,C1A1,C1A1bis = Arc('B-5->C',B1,C1,5),Arc('C-8->A',C1,A1,8),Arc('C-1->A',C1,A1,1) >>> g2 = Graphe('graphe2') >>> g2.setNoeuds([A1,B1,C1]) >>> g2.setArcs([A1B1,A1B1bis,A1C1,B1C1,C1A1,C1A1bis]) >>> print(g2) Graphe(nom=graphe2, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A']) >>> # Il y a 6 cycles distincts partant de A1 >>> # On les affiche tous, ils semblent en doublon mais ce sont les arcs les liant qui diffèrent. >>> for cycle in g2.getAllNodesCyclesFromNoeud(A1): ... print("====cycle====") ... for n in cycle: ... print(n.getNom()) ... ====cycle==== A B C A ====cycle==== A B C A ====cycle==== A B C A ====cycle==== A B C A ====cycle==== A C A ====cycle==== A C A """ # noqa : E501 pass assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud." # noqa : E501 allCycles = [] if self.hasCycleFromNoeud(noeudDepart): for noeud in self.getCreanciers(noeudDepart): for i in range(len(self.getNodesWays(noeud, noeudDepart))): cycle = [] cycle = [noeudDepart] cycle.extend(self.getNodesWays(noeud, noeudDepart)[i]) allCycles.append(cycle) return allCycles def getArcsBetween(self, noeudDepart: Noeud, noeudArrivee: Noeud) -> list: """Renvoie tous les arcs entre noeudDepart et noeudArrivee. Paramètres ---------- - noeudDepart : Noeud - Le noeud de départ des arcs. - noeudArrivee : Noeud - Le noeud de d'arrivée des arcs. Tests ----- >>> g = Graphe('graphe') >>> A,B,D,E = Noeud('A'),Noeud('B'),Noeud('D'),Noeud('E') >>> AB1,AB2,AB3 = Arc('A->B(1)',A,B,1),Arc('A->B(2)',A,B,1),Arc('A->B(3)',A,B,1) >>> DE = Arc('D->E',D,E,1) >>> EA = Arc('E->A',E,A,1) >>> g.setNoeuds([A,B,D,E]) >>> g.setArcs([AB1,AB2,AB3,DE,EA]) >>> for a in g.getArcsBetween(A,B): ... print(a.getNom()) ... A->B(1) A->B(2) A->B(3) >>> # Aucun arc de D à A >>> for a in g.getArcsBetween(D,A): ... print(a.getNom()) ... """ # noqa : E501 pass assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud." # noqa : E501 assert isinstance(noeudArrivee, Noeud), "Le noeudArrivee doit être un instance de la classe Noeud." # noqa : E501 arcs = [] for arc in self.getArcs(): if (arc.getNoeudDebut() == noeudDepart and arc.getNoeudFin() == noeudArrivee): arcs.append(arc) return arcs def getArcsWay(self, noeudDepart: Noeud, noeudArrivee: Noeud) -> list: """Renvoie une liste d'arcs contenant un chemin entre noeudDepart et noeudArrivee s'il en existe. Renvoie [] sinon. Paramètres ---------- - noeudDepart : Noeud - Le noeud de départ des arcs du chemin. - noeudArrivee : Noeud - Le noeud de d'arrivée des arcs du chemin. Remarque -------- Ne trouve pas les cycles Choisit un arc au hasard s'il existe plusieurs arcs parallèles. noeudDepart : Noeud - noeud de départ noeudArrivee : Noeud - noeud d'arrivée Tests ----- >>> g = Graphe('graphe') >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E') >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1) >>> CB = Arc('C->B',C,B,1) >>> CBbis = Arc('C-2->B',C,B,2) >>> DE = Arc('D->E',D,E,1) >>> EA = Arc('E->A',E,A,1) >>> g.setNoeuds([A,B,C,D,E]) >>> g.setArcs([AB,AC,AD,CB,CBbis,DE,EA]) >>> # Aucun chemin de B à C >>> for arc in g.getArcsWay(B,C): ... print(arc) ... >>> # Dans ce graphe trois chemins sont possibles entre A et B >>> # Deux des trois chemins présentent des arcs parallèles. >>> # On fixe la graine pour les tests >>> random.seed(13) >>> for arc in g.getArcsWay(A,B): ... print(arc.getNom()) ... A->C C->B >>> random.seed(27) >>> for arc in g.getArcsWay(A,B): ... print(arc.getNom()) ... A->C C-2->B >>> random.seed(3) >>> for arc in g.getArcsWay(A,B): ... print(arc.getNom()) ... A->B """ # noqa : E501 pass assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud." # noqa : E501 assert isinstance(noeudArrivee, Noeud), "Le noeudArrivee doit être un instance de la classe Noeud." # noqa : E501 if self.getNodesWays(noeudDepart, noeudArrivee): arcsWay = random.choice( self.getArcsWays(noeudDepart, noeudArrivee) ) else: arcsWay = [] return arcsWay def getArcsCycleFromNoeud(self, noeudDepart: Noeud) -> list: """Renvoie une liste d'arcs représentant un cycle partant du noeudDepart s'il en existe. [] sinon. Paramètres ---------- - noeudDepart : Noeud - Le noeud de départ du cycle. Remarque -------- S'il existe plusieurs cycles à partir d'un noeud, l'algorithme n'en renvoie qu'un seul au hasard. Tests ----- >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3) >>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1) >>> g2 = Graphe('graphe2') >>> g2.setNoeuds([A,B,C]) >>> g2.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) >>> print(g2) Graphe(nom=graphe2, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A']) >>> # Il y a 6 cycles partants de A dans ce graphe >>> # On les récupère tous en fixant la graine >>> random.seed(1) >>> for arc in g2.getArcsCycleFromNoeud(A): ... print(arc.getNom()) ... A-5->B B-5->C C-1->A >>> random.seed(2) >>> for arc in g2.getArcsCycleFromNoeud(A): ... print(arc.getNom()) ... A-5->B B-5->C C-8->A >>> random.seed(6) >>> for arc in g2.getArcsCycleFromNoeud(A): ... print(arc.getNom()) ... A-2->B B-5->C C-1->A >>> random.seed(4) >>> for arc in g2.getArcsCycleFromNoeud(A): ... print(arc.getNom()) ... A-2->B B-5->C C-8->A >>> random.seed(5) >>> for arc in g2.getArcsCycleFromNoeud(A): ... print(arc.getNom()) ... A-3->C C-1->A >>> random.seed(24) >>> for arc in g2.getArcsCycleFromNoeud(A): ... print(arc.getNom()) ... A-3->C C-8->A """ # noqa : E501 pass assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud." # noqa : E501 arcsCycle = [] if self.hasCycleFromNoeud(noeudDepart): noeudsPossibles = [] for noeud in self.getCreanciers(noeudDepart): if (self.getNodesWays(noeud, noeudDepart) and noeud not in noeudsPossibles): noeudsPossibles.append(noeud) # On choisit aléatoirement un premier noeud parmi ceux # qui permettent de revenir à noeudDepart noeudAleatoire = random.choice(noeudsPossibles) # On ajoute le début du chemin arcsCycle.extend(self.getArcsWay(noeudDepart, noeudAleatoire)) # On ajoute le reste du chemin arcsCycle.extend(self.getArcsWay(noeudAleatoire, noeudDepart)) return arcsCycle def getMinPoidsFromCycle(self, cycle: list) -> int: """Renvoie le plus petits poids des arcs contenus dans cycle. Paramètres ---------- cycle : list - Une liste d'arcs du graphe. Remarque -------- Ici on gère les éventuels cycles parallèles Tests ----- >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3) >>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1) >>> g = Graphe('graphe') >>> g.setNoeuds([A,B,C]) >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A']) >>> random.seed(None) >>> random.seed(9) >>> cycle = g.getArcsCycleFromNoeud(A) >>> for e in cycle: ... print(e.getNom()) ... A-3->C C-1->A >>> g.getMinPoidsFromCycle(cycle) 1 >>> # Sans simplifier le graphe il peut y avoir des cycles parallèles en partant d'un noeud >>> random.seed(15) >>> cycle = g.getArcsCycleFromNoeud(A) >>> for e in cycle: ... print(e.getNom()) ... A-5->B B-5->C C-8->A >>> g.getMinPoidsFromCycle(cycle) 5 >>> random.seed(13) >>> cycle = g.getArcsCycleFromNoeud(A) >>> for e in cycle: ... print(e.getNom()) ... A-2->B B-5->C C-8->A >>> g.getMinPoidsFromCycle(cycle) 2 >>> random.seed(17) >>> cycle = g.getArcsCycleFromNoeud(A) >>> for e in cycle: ... print(e.getNom()) ... A-2->B B-5->C C-1->A >>> g.getMinPoidsFromCycle(cycle) 1 >>> # Un test avec simplification préalable, il n'y a donc plus de cycles parallèles en partant d'un noeud. >>> # Mais il peut rester plusieurs cycles partant de A ! >>> g1 = Graphe('graphe1') >>> g1.setNoeuds([A,B,C]) >>> g1.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) >>> print(g1) Graphe(nom=graphe1, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A']) >>> g1.simplification() >>> print(g1) Graphe(nom=graphe1, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A']) >>> cycle = g1.getArcsCycleFromNoeud(A) >>> for e in cycle: ... print(e.getNom()) ... A-7->B B-5->C C-9->A >>> g1.getMinPoidsFromCycle(cycle) 5 """ # noqa : E501 pass # les éléments de cycle doivent être des instances de la classe Arc # Ne pas utiliser les assertions pour valider les données # car elles peuvent être désactivées. # On lève donc une erreur en cas de souci # Un booléen pour tester si tout est OK allOk = True for arc in cycle: assert (isinstance(arc, Arc) for arc in cycle), "Les éléments de cycle doivent être des instances de la classe Arc" # noqa : E501 if not isinstance(arc, Arc): allOk = False raise ValueError(f'Les éléments de cycle doivent être des instances de la classe Arc. Vérifier : {arc.getNom()}') # noqa : E501 assert len(cycle) >= 1, "Le cycle doit avoir au moins un élément." if len(cycle) < 1: allOk = False raise ValueError('Le cycle doit avoir au moins un élément.') if allOk: poidsMin = cycle[0].getPoids() i = 1 while i < len(cycle): if cycle[i].getPoids() <= poidsMin: poidsMin = cycle[i].getPoids() i += 1 return poidsMin def eliminerCycleFromNoeud(self, noeudDepart: Noeud) -> None: """Renvoie l'instance du graphe modifiée. Si le graphe contient un cycle à partir de noeudDepart, les arcs de ce cycle sont réduits du plus petit poids des arcs. Sinon le graphe n'est pas modifié. Paramètres ---------- noeudDepart : Noeud - Le noeud de départ du cycle à éliminer. Remarque -------- Lorsque le graphe présente un cycle à partir de noeudDepart, cela revient à le supprimer. Tests ----- >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3) >>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1) >>> g = Graphe('graphe') >>> g.setNoeuds([A,B,C]) >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A']) >>> g.simplification() >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A']) >>> # Ce graphe contient 2 cycles à partir de A >>> for cycle in g.getAllNodesCyclesFromNoeud(A): ... print("====cycle====") ... for noeud in cycle: ... print(noeud.getNom()) ... ====cycle==== A B C A ====cycle==== A C A >>> # On élimine le premier, il reste le second >>> random.seed(7) >>> g.eliminerCycleFromNoeud(A) >>> for cycle in g.getAllNodesCyclesFromNoeud(A): ... print("====cycle====") ... for noeud in cycle: ... print(noeud.getNom()) ... ====cycle==== A C A >>> # On élimine le second >>> g.eliminerCycleFromNoeud(A) >>> # Il n'y a plus de cycle partant de A >>> for cycle in g.getAllNodesCyclesFromNoeud(A): ... print("====cycle====") ... for noeud in cycle: ... print(noeud.getNom()) ... """ # noqa : E501 pass assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud." # noqa : E501 if self.hasCycleFromNoeud(noeudDepart): cycleToReduce = self.getArcsCycleFromNoeud(noeudDepart) minPoids = self.getMinPoidsFromCycle(cycleToReduce) for arc in cycleToReduce: arc.setPoids(arc.getPoids()-minPoids) # On renomme l'arc nouveauNom = arc.getNoeudDebut().getNom()+'-'+str(arc.getPoids())+'->'+arc.getNoeudFin().getNom() # noqa : E501 arc.setNom(nouveauNom) if arc.getPoids() == 0: self.removeArc(arc) def hasArcInverse(self, arcCandidat: Arc) -> bool: """Renvoie un booléen selon que le graphe contient un arc inverse de arcCandidat ou pas. Paramètres ---------- arcCandidat : Arc - L'arc dont on veut savoir si il a un arc inverse. Remarques --------- - L'arc inverse n'est pas forcément un arc inverse dans le sens que l'arc d'un cycle de Klein pour être un arc inverse introduit au debut de l'algorithme. - On suppose que le graphe est simplifié au préalable afin qu'il n'y ait qu'un seul arc entre deux noeuds dans un sens donné. Tests ----- >>> g = Graphe('graphe') >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E') >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1) >>> BA = Arc('B->A',B,A,1) >>> CB = Arc('C->B',C,B,1) >>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1) >>> EA = Arc('E->A',E,A,1) >>> g.setNoeuds([A,B,C,D,E]) >>> g.setArcs([AB,AC,AD,BA,CB,DE,DB,EA]) >>> g.hasArcInverse(AB) False >>> # On ajoute tous les arcs inverses au graphe >>> g.addArcsInversesNuls() >>> g.hasArcInverse(AB) True """ pass assert isinstance(arcCandidat, Arc), "arcCandidat doit être un instance de la classe Arc." # noqa : E501 boolDeSortie = False if not arcCandidat.getEstInverse(): for arc in self.getArcsInverses(): if (arcCandidat.getNoeudDebut() == arc.getNoeudFin() and arcCandidat.getNoeudFin() == arc.getNoeudDebut()): boolDeSortie = True if arcCandidat.getEstInverse(): for arc in self.getArcsDirects(): if (arcCandidat.getNoeudDebut() == arc.getNoeudFin() and arcCandidat.getNoeudFin() == arc.getNoeudDebut()): boolDeSortie = True return boolDeSortie def getArcInverse(self, arcCandidat: Arc) -> Arc: """Renvoie l'arc inverse d'arcCandidat s'il en a un, plante sinon. Paramètres ---------- arcCandidat : Arc - L'arc dont on veut récupérer l'arc inverse. Remarques --------- - L'arc inverse n'est pas forcément un arc inverse dans le sens que l'arc d'un cycle de Klein peut être un arc inverse introduit au debut de l'algorithme. - On suppose que le graphe est simplifié au préalable afin qu'il n'y ait qu'un seul arc entre deux noeuds dans un sens donné. Tests ----- >>> g = Graphe('graphe') >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E') >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1) >>> BA = Arc('B->A',B,A,1) >>> CB = Arc('C->B',C,B,1) >>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1) >>> EA = Arc('E->A',E,A,1) >>> g.setNoeuds([A,B,C,D,E]) >>> g.setArcs([AB,AC,AD,BA,CB,DE,DB,EA]) >>> # On ajoute tous les arcs inverses au graphe >>> g.addArcsInversesNuls() >>> print(AB.getNom()) A->B >>> print(g.getArcInverse(AB).getNom()) B-0->A """ pass assert isinstance(arcCandidat, Arc), "arcCandidat doit être un instance de la classe Arc." # noqa : E501 assert self.hasArcInverse(arcCandidat), "arcCandidat doit avoir un arc inverse." # noqa : E501 arcInverse = None if self.hasArcInverse(arcCandidat): if not arcCandidat.getEstInverse(): for arc in self.getArcsInverses(): if (arc.getNoeudFin() == arcCandidat.getNoeudDebut() and arc.getNoeudDebut() == arcCandidat.getNoeudFin()): arcInverse = arc if arcCandidat.getEstInverse(): for arc in self.getArcsDirects(): if (arc.getNoeudFin() == arcCandidat.getNoeudDebut() and arc.getNoeudDebut() == arcCandidat.getNoeudFin()): arcInverse = arc return arcInverse def addArcsInversesNuls(self) -> None: """Ajoute un arc inverse de valeur 0 pour chaque arc du graphe. Remarque -------- Si le graphe n'est pas simplifié, on le simplifie d'abord. Tests ----- >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3) >>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1) >>> g = Graphe('graphe') >>> g.setNoeuds([A,B,C]) >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A']) >>> for arc in g.getArcs(): ... print(arc) ... Arc(nom=A-5->B, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), noeudFin=Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=0), poids=5, estInverse=False) Arc(nom=A-2->B, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), noeudFin=Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=0), poids=2, estInverse=False) Arc(nom=A-3->C, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), noeudFin=Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0), poids=3, estInverse=False) Arc(nom=B-5->C, noeudDebut=Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=0), noeudFin=Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0), poids=5, estInverse=False) Arc(nom=C-8->A, noeudDebut=Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0), noeudFin=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), poids=8, estInverse=False) Arc(nom=C-1->A, noeudDebut=Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0), noeudFin=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), poids=1, estInverse=False) >>> g.addArcsInversesNuls() >>> for arc in g.getArcs(): ... print(arc) ... Arc(nom=A-7->B, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), noeudFin=Noeud(nom=B, voisins=['A', 'C'], nbArcs=4, capital=0), poids=7, estInverse=False) Arc(nom=A-3->C, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), noeudFin=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), poids=3, estInverse=False) Arc(nom=B-5->C, noeudDebut=Noeud(nom=B, voisins=['A', 'C'], nbArcs=4, capital=0), noeudFin=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), poids=5, estInverse=False) Arc(nom=C-9->A, noeudDebut=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), noeudFin=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), poids=9, estInverse=False) Arc(nom=B-0->A, noeudDebut=Noeud(nom=B, voisins=['A', 'C'], nbArcs=4, capital=0), noeudFin=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), poids=0, estInverse=True) Arc(nom=C-0->A, noeudDebut=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), noeudFin=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), poids=0, estInverse=True) Arc(nom=C-0->B, noeudDebut=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), noeudFin=Noeud(nom=B, voisins=['A', 'C'], nbArcs=4, capital=0), poids=0, estInverse=True) Arc(nom=A-0->C, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), noeudFin=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), poids=0, estInverse=True) """ # noqa : E501 pass assert self.hasArc(), "Le graphe doit avoir des arcs." if not self.estSimplifie(): self.simplification() newArcs = [] for arc in self.getArcs(): newName = "{}-0->{}".format( arc.getNoeudFin().getNom(), arc.getNoeudDebut().getNom() ) newArcInverseNul = Arc( newName, arc.getNoeudFin(), arc.getNoeudDebut(), 0, True ) newArcs.append(newArcInverseNul) for arc in newArcs: self.addArc(arc) # ================================================================ # =========== METHODES SPECIFIQUES ALGO KLEIN ==================== # ================================================================ def kleinHasCycleFromNoeud(self, noeudCandidat: Noeud) -> bool: """Renvoie un booléen selon que le graphe présente un cycle de Klein ou non à partir du noeudCandidat. Paramètres ---------- noeudCandidat : Noeud - Une instance de la classe Noeud. Remarque -------- Un cycle de Klein est un cycle dont tous les arcs ont un poids non nul. Tests ----- >>> g = Graphe('graphe') >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E') >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1) >>> BA = Arc('B->A',B,A,1) >>> CB = Arc('C->B',C,B,1) >>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1) >>> EA = Arc('E->A',E,A,1) >>> g.setNoeuds([A,B,C,D,E]) >>> g.setArcs([AB,AC,AD,BA,CB,DE,DB,EA]) >>> g.kleinHasCycleFromNoeud(A) True >>> AB.setPoids(0) >>> g.kleinHasCycleFromNoeud(A) True >>> BA.setPoids(0) >>> g.kleinHasCycleFromNoeud(A) True >>> DE.setPoids(0) >>> g.kleinHasCycleFromNoeud(A) False """ pass assert isinstance(noeudCandidat, Noeud), "Le noeudCandidat doit être un instance de la classe Noeud." # noqa : E501 # On récupère tous les noeuds situés à un arc de noeudCancidat noeudDirectementAccessibles = self.getCreanciers(noeudCandidat) boolDeSortie = False for noeud in noeudDirectementAccessibles: if self.kleinArcsBetween(noeudCandidat, noeud): if self.kleinAllArcsWays(noeud, noeudCandidat): boolDeSortie = True return boolDeSortie def kleinHasCycle(self) -> None: """Renvoie un booléen selon que le graphe a encore au moins un cycle de Klein. Remarque -------- Un cycle de Klein est un cycle dont tous les arcs ont un poids non nul. Tests ----- >>> g = Graphe('graphe') >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E') >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1) >>> BA = Arc('B->A',B,A,1) >>> CB = Arc('C->B',C,B,1) >>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1) >>> EA = Arc('E->A',E,A,1) >>> g.setNoeuds([A,B,C,D,E]) >>> g.setArcs([AB,AC,AD,BA,CB,DE,DB,EA]) >>> g.kleinHasCycle() True >>> AB.setPoids(0) >>> g.kleinHasCycle() True >>> BA.setPoids(0) >>> g.kleinHasCycle() True >>> DE.setPoids(0) >>> g.kleinHasCycle() False """ # noqa : E501 pass boolDeSortie = False index = 0 noeudsDuGraphe = self.getNoeuds() while (not self.kleinHasCycleFromNoeud(noeudsDuGraphe[index]) and index < len(noeudsDuGraphe)-1): index += 1 if index < len(noeudsDuGraphe)-1: boolDeSortie = True return boolDeSortie def kleinArcsBetween( self, noeudDepart: Noeud, noeudArrivee: Noeud ) -> list: """Renvoie tous les arcs de poids non nul entre noeudDepart et noeudArrivee. Paramètres ---------- - noeudDepart : Noeud - Le noeud de départ des arcs. - noeudArrivee : Noeud - Le noeud de d'arrivée des arcs. Remarque -------- Méthode utile pour l'algorithme de Klein Tests ----- >>> g = Graphe('graphe') >>> A,B,D,E = Noeud('A'),Noeud('B'),Noeud('D'),Noeud('E') >>> AB1,AB2,AB3 = Arc('A->B(1)',A,B,0),Arc('A->B(2)',A,B,1),Arc('A->B(3)',A,B,1) >>> DEg = Arc('D->E',D,E,1) >>> EAg = Arc('E->A',E,A,1) >>> g.setNoeuds([A,B,D,E]) >>> g.setArcs([AB1,AB2,AB3,DEg,EAg]) >>> for a in g.kleinArcsBetween(A,B): ... print(a.getNom()) ... A->B(2) A->B(3) >>> # Aucun arc de D à A >>> for a in g.kleinArcsBetween(D,A): ... print(a.getNom()) ... >>> g1 = Graphe('graphe1') >>> A1,B1,C1,D1,E1 = Noeud('A1'),Noeud('B1'),Noeud('C1'),Noeud('D1'),Noeud('E1') >>> AB,AC,AD = Arc('A1->B1',A1,B1,1),Arc('A1->C1',A1,C1,1),Arc('A1->D1',A1,D1,1) >>> ABbis = Arc('A1->B1(2)',A1,B1,2) >>> CB = Arc('C1->B1',C1,B1,1) >>> DE,DB = Arc('D1->E1',D1,E1,1),Arc('D1->B1',D1,B1,1) >>> EA = Arc('E1->A1',E1,A1,1) >>> EB = Arc('E1->B1',E1,B1,1) >>> g1.setNoeuds([A1,B1,C1,D1,E1]) >>> g1.setArcs([AB,AC,AD,ABbis,CB,DE,DB,EA,EB]) >>> # print(g1.kleinArcsBetween(A1,B1)) >>> for a in g1.kleinArcsBetween(A1,B1): ... print(a.getNom()) ... A1->B1 A1->B1(2) """ # noqa : E501 pass assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud." # noqa : E501 assert isinstance(noeudArrivee, Noeud), "Le noeudArrivee doit être un instance de la classe Noeud." # noqa : E501 arcs = [] for arc in self.getArcs(): if (arc.getNoeudDebut() == noeudDepart and arc.getNoeudFin() == noeudArrivee and arc.getPoids() != 0): arcs.append(arc) return arcs def kleinAllArcsWays( self, noeudDepart: Noeud, noeudArrivee: Noeud ) -> list: """Renvoie une liste de tuples : - d'arcs de poids non nul - et/ou d'arcs inverses de poids non nul - ne passant pas deux fois par le même noeud contenant un chemin entre noeudDepart et noeudArrivee s'il en existe. Renvoie [] sinon. Paramètres ---------- - noeudDepart : Noeud - Le noeud de départ des arcs des chemins. - noeudArrivee : Noeud - Le noeud de d'arrivée des arcs des chemins. Remarques --------- - Ne trouve pas les cycles. - Méthode utile pour l'algorithme de Klein. - On ne peut pas supprimer de chemins ici en amont de la recherche de cycles noeudDepart : Noeud - noeud de départ noeudArrivee : Noeud - noeud d'arrivée Tests ----- >>> g = Graphe('graphe') >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E') >>> AB1,AC,AD = Arc('A->B(1)',A,B,0),Arc('A->C',A,C,1),Arc('A->D',A,D,1) >>> ABi = Arc('A->B(i)',A,B,1,True) >>> BA1 = Arc('B->A(1)',B,A,0) >>> BAi = Arc('B->A(i)',B,A,2,True) >>> CB = Arc('C->B',C,B,1) >>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1) >>> EA,EB = Arc('E->A',E,A,1),Arc('E->B',E,B,1) >>> g.setNoeuds([A,B,C,D,E]) >>> g.setArcs([AB1,ABi,AC,AD,BA1,BAi,CB,DE,DB,EA,EB]) >>> g.estSimplifie() True >>> # Deux chemins de Klein de B à C >>> # Mais on ne veut que les chemins : >>> # --> qui ont des arcs directs de poids non nul >>> # --> et/ou des arcs inverses de poids non nul >>> # Cela n'en laisse qu'un >>> for chemin in g.kleinAllArcsWays(B,C): ... print("=== chemin de Klein ===") ... for arc in chemin: ... print(arc.getNom()) ... === chemin de Klein === B->A(i) A->C >>> # Dans ce graphe cinq chemins sont possibles entre A et B >>> # Mais on ne veut que les chemins : >>> # --> qui ont des arcs directs de poids non nul >>> # --> et/ou des arcs inverses de poids non nul >>> # Cela n'en laisse que quatre >>> for chemin in g.kleinAllArcsWays(A,B): ... print("=== chemin de Klein ===") ... for arc in chemin: ... print(arc.getNom()) === chemin de Klein === A->B(i) === chemin de Klein === A->C C->B === chemin de Klein === A->D D->E E->B === chemin de Klein === A->D D->B >>> # On remet un poids non nul sur les arcs AB1 pour retrouver les cinq chemins >>> AB1.setPoids(1) >>> for chemin in g.kleinAllArcsWays(A,B): ... print("=== chemin de Klein ===") ... for arc in chemin: ... print(arc.getNom()) === chemin de Klein === A->B(1) === chemin de Klein === A->B(i) === chemin de Klein === A->C C->B === chemin de Klein === A->D D->E E->B === chemin de Klein === A->D D->B >>> # Si on mets un poids nul ailleurs que sur le premier arc d'un chemin >>> # Le chemin doit être supprimé des possibles aussi >>> # Cela laisse quatre chemins valides >>> EB.setPoids(0) >>> for chemin in g.kleinAllArcsWays(A,B): ... print("=== chemin de Klein ===") ... for arc in chemin: ... print(arc.getNom()) === chemin de Klein === A->B(1) === chemin de Klein === A->B(i) === chemin de Klein === A->C C->B === chemin de Klein === A->D D->B """ # noqa : E501 pass assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud." # noqa : E501 assert isinstance(noeudArrivee, Noeud), "Le noeudArrivee doit être un instance de la classe Noeud." # noqa : E501 assert noeudDepart != noeudArrivee, "noeudDepart et noeudArrivee doivent être distincts." # noqa : E501 assert self.estSimplifie(), "Les arcs directs du graphe doivent être simplifiés, fusionnés s'ils sont parallèles." # noqa : E501 # Si il existe au moins un chemin entre noeudDepart et noeudArrivee # On ne garde que les chemins : # -> dont les arcs ont des poids non nuls # -> dont les arcs inverses ont des poids non nuls # -> ne passant pas deux fois par le même noeud arcsWays = self.getArcsWays(noeudDepart, noeudArrivee) if arcsWays: # les chemins à garder, a priori vide arcsWaysOk = [] for arcsWay in arcsWays: wayToKeep = True noeudsConnus = [noeudDepart] for arc in arcsWay: if (arc.getPoids() == 0 or arc.getNoeudFin() in noeudsConnus): wayToKeep = False if wayToKeep: noeudsConnus.append(arc.getNoeudFin()) noeudsConnus = [] if wayToKeep: arcsWaysOk.append(arcsWay) else: arcsWaysOk = [] return arcsWaysOk def kleinArcsWay( self, noeudDepart: Noeud, noeudArrivee: Noeud ) -> list: """Renvoie une liste : - d'arcs de poids non nul - et/ou d'arcs inverses de poids non nul - ne passant pas deux fois par le même noeud constituant un chemin entre noeudDepart et noeudArrivee s'il en existe. Renvoie [] sinon. Paramètres ---------- - noeudDepart : Noeud - Le noeud de départ des arcs du chemin. - noeudArrivee : Noeud - Le noeud de d'arrivée des arcs du chemin. Remarque -------- Ne trouve pas les cycles. Méthode utile pour l'algorithme de Klein. noeudDepart : Noeud - noeud de départ noeudArrivee : Noeud - noeud d'arrivée Tests ----- >>> g = Graphe('graphe') >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E') >>> AB1,AC,AD = Arc('A->B(1)',A,B,0),Arc('A->C',A,C,1),Arc('A->D',A,D,1) >>> ABi = Arc('A->B(i)',A,B,1,True) >>> BA1 = Arc('B->A(1)',B,A,0) >>> BAi = Arc('B->A(i)',B,A,2,True) >>> CB = Arc('C->B',C,B,1) >>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1) >>> EA,EB = Arc('E->A',E,A,1),Arc('E->B',E,B,1) >>> g.setNoeuds([A,B,C,D,E]) >>> g.setArcs([AB1,ABi,AC,AD,BA1,BAi,CB,DE,DB,EA,EB]) >>> g.estSimplifie() True >>> # Un seul chemin de Klein de B à C >>> for chemin in g.kleinAllArcsWays(B,C): ... print("=== chemin de Klein ===") ... for arc in chemin: ... print(arc.getNom()) ... === chemin de Klein === B->A(i) A->C >>> # Dans ce graphe cinq chemins sont possibles entre A et B >>> # Mais on ne veut que les chemins avec >>> # des arcs directs de poids non nul >>> # et/ou des arcs inverses de poids non nul >>> # Cela n'en laisse que quatre >>> for chemin in g.kleinAllArcsWays(A,B): ... print("=== chemin de Klein ===") ... for arc in chemin: ... print(arc.getNom()) ... === chemin de Klein === A->B(i) === chemin de Klein === A->C C->B === chemin de Klein === A->D D->E E->B === chemin de Klein === A->D D->B >>> # On fixe la graine pour en avoir un au hasard >>> random.seed(1) >>> for arc in g.kleinArcsWay(A,B): ... print(arc.getNom()) A->C C->B >>> # On remet un poids non nul sur les arcs AB1, pour retrouver les cinq chemins >>> AB1.setPoids(1) >>> for chemin in g.kleinAllArcsWays(A,B): ... print("=== chemin de Klein ===") ... for arc in chemin: ... print(arc.getNom()) ... === chemin de Klein === A->B(1) === chemin de Klein === A->B(i) === chemin de Klein === A->C C->B === chemin de Klein === A->D D->E E->B === chemin de Klein === A->D D->B >>> # On fixe la graine pour en avoir un au hasard >>> random.seed(1) >>> for arc in g.kleinArcsWay(A,B): ... print(arc.getNom()) A->B(i) >>> # Si on mets un poids nul ailleurs que sur le premier arc d'un chemin >>> # Le chemin doit être supprimé des possibles aussi >>> # Cela laisse quatre cemins valides >>> EB.setPoids(0) >>> for chemin in g.kleinAllArcsWays(A,B): ... print("=== chemin de Klein ===") ... for arc in chemin: ... print(arc.getNom()) ... === chemin de Klein === A->B(1) === chemin de Klein === A->B(i) === chemin de Klein === A->C C->B === chemin de Klein === A->D D->B >>> # On a fixé la graine pour en avoir un au hasard >>> for arc in g.kleinArcsWay(A,B): ... print(arc.getNom()) A->B(1) """ # noqa : E501 pass assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud." # noqa : E501 assert isinstance(noeudArrivee, Noeud), "Le noeudArrivee doit être un instance de la classe Noeud." # noqa : E501 assert noeudDepart != noeudArrivee, "noeudDepart et noeudArrivee doivent être distincts." # noqa : E501 assert self.estSimplifie(), "Les arcs directs du graphe doivent être simplifiés, fusionnés s'ils sont parallèles." # noqa : E501 arcsWayOk = [] kleinArcsWays = self.kleinAllArcsWays(noeudDepart, noeudArrivee) if kleinArcsWays: arcsWayOk = random.choice(kleinArcsWays) return arcsWayOk def kleinAllArcsCyclesFromNoeud(self, noeudDepart: Noeud) -> list: """Renvoie une liste de liste d'arcs représentant un cycle de Klein valide s'il en existe Renvoie [] sinon. Paramètres ---------- - noeudDepart : Noeud - Le noeud de départ des arcs des cycles. Remarques --------- - Le graphe constitué par les arcs directs doit être simplifié. - La méthode kleinAllArcsWays() exclut déjà les chemin passant deux fois par le même noeud. - On va ici encore exclure certains chemins cycliques à savoir : - ceux qui n'ont que deux arcs qui sont inverses l'un de l'autre - ceux qui n'ont que des arcs inverses - ceux qui ont un poids algébrique négatif. - Le poids algébrique est obtenu en ajouter le poids des arcs directs et en enlevant le poids des arcs inverses. Tests ----- >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3) >>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1) >>> g = Graphe('graphe') >>> g.setNoeuds([A,B,C]) >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A']) >>> g.estSimplifie() False >>> g.simplification() >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A']) >>> g.estSimplifie() True >>> # Il y a 2 cycles partants de A dans ce graphe >>> # Ce sont aussi des cycles de Klein >>> for cycle in g.kleinAllArcsCyclesFromNoeud(A): ... print("=== cycle ===") ... for arc in cycle: ... print(arc.getNom()) ... === cycle === A-7->B B-5->C C-9->A === cycle === A-3->C C-9->A >>> # On ajoute les arcs inverses manuellement pour les tests >>> BAi, CAi,CBi,ACi = Arc('B-0->A(i)',B,A,0,True),Arc('C-1->A(i)',C,A,1,True),Arc('C-2->B(i)',C,B,2,True),Arc('A-3->C(i)',A,C,3,True) >>> g.addArcs([BAi,CAi,CBi,ACi]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A', 'B-0->A(i)', 'C-1->A(i)', 'C-2->B(i)', 'A-3->C(i)']) >>> for cycle in g.kleinAllArcsCyclesFromNoeud(A): ... print("=== cycle ===") ... for arc in cycle: ... print(arc.getNom()) === cycle === A-7->B B-5->C C-9->A === cycle === A-7->B B-5->C C-1->A(i) === cycle === A-3->C C-9->A >>> # Tests sur un autre graphe >>> D,E,F,G = Noeud('D'),Noeud('E'),Noeud('F'),Noeud('G') >>> DE,EF,FG,FD = Arc('D-1->E',D,E,1),Arc('E-2->F',E,F,2),Arc('F-3->G',F,G,3),Arc('F-4->D',F,D,4) >>> # On ajoute un arc inverse manuellement >>> GFi = Arc('G-3->F(i)',G,F,3,True) >>> g1 = Graphe('graphe1') >>> g1.setNoeuds([D,E,F,G]) >>> g1.setArcs([DE,EF,FG,FD,GFi]) >>> # Ne présente qu'un cycle DEFD, >>> # Le chemin DEFGFD est exclu par la méthode kleinAllArcsWays() >>> for cycle in g1.kleinAllArcsCyclesFromNoeud(D): ... print("=== cycle ===") ... for arc in cycle: ... print(arc.getNom()) ... === cycle === D-1->E E-2->F F-4->D >>> # Quelques arcs en plus qui pourraient poser problème >>> FE,ED = Arc('F-5->E',F,E,5),Arc('E-6->D',E,D,6) >>> FEi,EFi,DEi,EDi = Arc('F-2->E(i)',F,E,2,True),Arc('E-5->F(i)',E,F,5,True),Arc('D-6->E(i)',D,E,6,True),Arc('E-1->D(i)',E,D,1,True) >>> g1.addArcs([FE,ED,FEi,EFi,DEi,EDi]) >>> # Il y a 2 cycles valides à partir de D >>> for cycle in g1.kleinAllArcsCyclesFromNoeud(D): ... print("=== cycle ===") ... for arc in cycle: ... print(arc.getNom()) ... === cycle === D-1->E E-2->F F-4->D === cycle === D-1->E E-6->D """ # noqa : E501 pass # On récupère tous les chemins cycliques allArcsKleinCycles = [] if self.kleinHasCycleFromNoeud(noeudDepart): noeudsPossibles = [] for noeud in self.getCreanciers(noeudDepart): if (self.kleinAllArcsWays(noeud, noeudDepart) and noeud not in noeudsPossibles): noeudsPossibles.append(noeud) # Pour tous les noeuds qui permettent de revenir à noeudDepart for noeudPossible in noeudsPossibles: arcsKleinCycle = [] # Pour tous les chemins entre un noeudPossible et noeudDepart suiteChemin = self.kleinAllArcsWays(noeudPossible, noeudDepart) # noqa : E501 for chemin in suiteChemin: # pour tous les arcs qui permettent de joindre noeudDepart # et noeudPossible debutChemin = self.kleinArcsBetween(noeudDepart, noeudPossible) # noqa : E501 for premierArc in debutChemin: # On remet à zéro arcsKleinCycle = [] # On ajoute le premier arc arcsKleinCycle.extend([premierArc]) # On ajoute le reste du chemin arcsKleinCycle.extend(chemin) # On évite les doublons if arcsKleinCycle not in allArcsKleinCycles: allArcsKleinCycles.append(arcsKleinCycle) # Il faut encore nettoyer de certains chemins : # -> ceux qui n'ont que deux arcs qui sont inverses # l'un de l'autre # -> ceux qui n'ont que des arcs inverses # -> ceux qui ont un poids algébrique négatif. # ======== Remarque # On a besoin d'un variable globale pour éviter l'erreur # UnboundLocalError: local variable referenced before assignment python # noqa : E501 # ======== global allArcsKleinCyclesCleaned allArcsKleinCyclesCleaned = [] for arcsKleinCycle in allArcsKleinCycles: cycleToKeep = True # ceux qui n'ont que deux arcs qui sont inverses # l'un de l'autre if len(arcsKleinCycle) == 2: arc0 = arcsKleinCycle[0] arc1 = arcsKleinCycle[1] if self.hasArcInverse(arc1): if arc0 == self.getArcInverse(arc1): cycleToKeep = False if self.hasArcInverse(arc0): if arc1 == self.getArcInverse(arc0): cycleToKeep = False # ceux qui n'ont que des arcs inverses onlyArcsInverses = True for arc in arcsKleinCycle: if not arc.getEstInverse(): onlyArcsInverses = False if onlyArcsInverses: cycleToKeep = False # ceux qui ont un poids algébrique négatif cycleWeight = 0 for arc in arcsKleinCycle: if arc.getEstInverse(): cycleWeight -= arc.getPoids() if not arc.getEstInverse(): cycleWeight += arc.getPoids() if cycleWeight <= 0: cycleToKeep = False if cycleToKeep: allArcsKleinCyclesCleaned.append(arcsKleinCycle) return allArcsKleinCyclesCleaned def kleinArcsCycleFromNoeud(self, noeudDepart: Noeud) -> list: """Renvoie une liste d'arcs représentant un cycle de Klein partant du noeudDepart s'il en existe. Renvoie [] sinon. Paramètres ---------- - noeudDepart : Noeud - Le noeud de départ des arcs du cycle. Remarques --------- - S'il existe plusieurs cycles à partir d'un noeud, l'algorithme n'en renvoie qu'un seul au hasard. - La recherche de cycles de Klein sous-entend que le graphe est simplifié, c'est à dire que les arcs parallèles entre deux noeuds soient fucionnés. - Un cycle ne doit pas passer plusieurs fois par le même noeud Tests ----- >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3) >>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1) >>> g = Graphe('graphe') >>> g.setNoeuds([A,B,C]) >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A']) >>> g.estSimplifie() False >>> g.simplification() >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A']) >>> g.estSimplifie() True >>> # Il y a 2 cycles partants de A dans ce graphe >>> # Ce sont aussi des cycles de Klein >>> # On les récupère tous en fixant la graine >>> random.seed(1) >>> for arc in g.kleinArcsCycleFromNoeud(A): ... print(arc.getNom()) ... A-7->B B-5->C C-9->A >>> random.seed(5) >>> for arc in g.kleinArcsCycleFromNoeud(A): ... print(arc.getNom()) ... A-3->C C-9->A >>> # On ajoute les arcs inverses manuellement pour les tests >>> BAi, CAi,CBi,ACi = Arc('B-0->A(i)',B,A,0,True),Arc('C-1->A(i)',C,A,1,True),Arc('C-2->B(i)',C,B,2,True),Arc('A-3->C(i)',A,C,3,True) >>> g.addArcs([BAi,CAi,CBi,ACi]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A', 'B-0->A(i)', 'C-1->A(i)', 'C-2->B(i)', 'A-3->C(i)']) >>> # En fixant la graine on teste pour vérifier qu'on obtient bien tous les cycles >>> for cycle in g.kleinAllArcsCyclesFromNoeud(A): ... print("=== cycle ===") ... for arc in cycle: ... print(arc.getNom()) ... === cycle === A-7->B B-5->C C-9->A === cycle === A-7->B B-5->C C-1->A(i) === cycle === A-3->C C-9->A >>> random.seed(1) >>> for arc in g.kleinArcsCycleFromNoeud(A): ... print(arc.getNom()) ... A-7->B B-5->C C-9->A >>> random.seed(5) >>> for arc in g.kleinArcsCycleFromNoeud(A): ... print(arc.getNom()) ... A-3->C C-9->A >>> random.seed(7) >>> for arc in g.kleinArcsCycleFromNoeud(A): ... print(arc.getNom()) ... A-7->B B-5->C C-1->A(i) """ # noqa : E501 pass assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud." # noqa : E501 arcsKleinCycle = [] if self.kleinHasCycleFromNoeud(noeudDepart): allCycles = self.kleinAllArcsCyclesFromNoeud(noeudDepart) arcsKleinCycle = random.choice(allCycles) return arcsKleinCycle def kleinAllArcsCycles(self) -> list: """Renvoie tous les cycles d'un graphe sans doublons. Remarques --------- - Notamment deux cycles sont considérés comme identiques s'ils contiennent globalement les mêmes arcs. Tests ----- >>> A, B, C = Noeud('A'), Noeud('B'), Noeud('C') >>> D, E, F = Noeud('D'), Noeud('E'), Noeud('F') >>> kleinAB, kleinBC = Arc('A-4->B', A, B, 4), Arc('B-6->C', B, C, 6) >>> kleinCD, kleinDE = Arc('C-5->D', C, D, 5), Arc('D-4->E', D, E, 4) >>> kleinEF, kleinFA = Arc('E-7->F', E, F, 7), Arc('F-5->A', F, A, 5) >>> kleinAE, kleinBE = Arc('A-2->E', A, E, 2), Arc('B-2->E', B, E, 2) >>> kleinDB, kleinFC = Arc('D-2->B', D, B, 2), Arc('F-3->C', F, C, 3) >>> G3 = Graphe('graphe3') >>> G3.setNoeuds([A, B, C, D, E, F]) >>> G3.setArcs([kleinAB, kleinBC, kleinCD, kleinDE, kleinEF]) >>> G3.addArcs([kleinFA, kleinAE, kleinBE, kleinDB, kleinFC]) >>> # Ce graphe compte 25 cycles si on n'élimine pas les doublons >>> # Mais 6 cycles distincts >>> print(len(G3.kleinAllArcsCycles())) 6 >>> for cycle in G3.kleinAllArcsCycles(): ... print('=== cycle unique ===') ... for arc in cycle: ... print(arc.getNom()) ... === cycle unique === A-4->B B-6->C C-5->D D-4->E E-7->F F-5->A === cycle unique === A-4->B B-2->E E-7->F F-5->A === cycle unique === A-2->E E-7->F F-5->A === cycle unique === B-6->C C-5->D D-2->B === cycle unique === B-2->E E-7->F F-3->C C-5->D D-2->B === cycle unique === C-5->D D-4->E E-7->F F-3->C """ pass # On récupère tous les noeuds noeuds = self.getNoeuds() # On récupère les cycles au fur et à mesure cyclesSansDoublons = [] # On initialise avec tous les cycles du premier noeud for cycle in self.kleinAllArcsCyclesFromNoeud(noeuds[0]): cyclesSansDoublons.append(cycle) # Pour chaque noeuds for noeud in noeuds: # Pour chaque cycle partant du noeud for cycle in self.kleinAllArcsCyclesFromNoeud(noeud): toAdd = True for cycleUnique in cyclesSansDoublons: if collections.Counter(cycle) == collections.Counter(cycleUnique): # noqa : E501 toAdd = False if toAdd: cyclesSansDoublons.append(cycle) return cyclesSansDoublons def kleinEstCycle(self, arcsCycle: list) -> bool: """Renvoie un booléen selon que arcsCycle est un cycle de Klein du graphe ou non. Remarque -------- On n'utilise pas la méthode kleinAllArcsCycles() ici car elle exclut les doublons or tous les cycles doivent être testés par exemple ABCA et BCAB sont un doublon du même cycle mais doivent être testés tous les deux. Paramètres ---------- arcsCycle : list - Une liste d'arcs candidate à être un cycle de Klein. Tests ----- >>> A, B, C = Noeud('A'), Noeud('B'), Noeud('C') >>> D, E, F = Noeud('D'), Noeud('E'), Noeud('F') >>> kleinAB, kleinBC = Arc('A-4->B', A, B, 4), Arc('B-6->C', B, C, 6) >>> kleinCD, kleinDE = Arc('C-5->D', C, D, 5), Arc('D-4->E', D, E, 4) >>> kleinEF, kleinFA = Arc('E-7->F', E, F, 7), Arc('F-5->A', F, A, 5) >>> kleinAE, kleinBE = Arc('A-2->E', A, E, 2), Arc('B-2->E', B, E, 2) >>> kleinDB, kleinFC = Arc('D-2->B', D, B, 2), Arc('F-3->C', F, C, 3) >>> G3 = Graphe('graphe3') >>> G3.setNoeuds([A, B, C, D, E, F]) >>> G3.setArcs([kleinAB, kleinBC, kleinCD, kleinDE, kleinEF]) >>> G3.addArcs([kleinFA, kleinAE, kleinBE, kleinDB, kleinFC]) >>> cycleOK = [kleinAB, kleinBC, kleinCD, kleinDE, kleinEF, kleinFA] >>> cycleKO = [kleinDB, kleinBE, Arc('E-1->D', E, D, 1)] >>> G3.kleinEstCycle(cycleOK) True >>> G3.kleinEstCycle(cycleKO) False """ pass for arc in arcsCycle: assert isinstance(arc, Arc), "Tous les éléments de arcsCycle doivent être des instances de la classe Arc." # noqa : E501 # booléen de sortie boolDeSortie = True # On récupère tous les cycles du graphe cyclesDuGraphe = [] for noeud in self.getNoeuds(): for cycle in self.kleinAllArcsCyclesFromNoeud(noeud): cyclesDuGraphe.append(cycle) if arcsCycle not in cyclesDuGraphe: boolDeSortie = False return boolDeSortie def kleinModifierCycle(self, arcsCycle: list) -> None: """Renvoie le graphe modifié après réduction du cycle. Remarque -------- - Chaque arc dans le sens du cycle est modifié en diminuant son poids du plus petit poids des arcs du cycle. - Chaque arc dans le sens inverse du cycle est aussi modifié en augmentant son poids du plus petit poids des arcs du cycle. Paramètres ---------- arcsCycle : list - Le cycle de Klein à réduire. Tests ----- >>> A, B, C = Noeud('A'), Noeud('B'), Noeud('C') >>> D, E, F = Noeud('D'), Noeud('E'), Noeud('F') >>> kleinAB, kleinBC = Arc('A-4->B', A, B, 4), Arc('B-6->C', B, C, 6) >>> kleinCD, kleinDE = Arc('C-5->D', C, D, 5), Arc('D-4->E', D, E, 4) >>> kleinEF, kleinFA = Arc('E-7->F', E, F, 7), Arc('F-5->A', F, A, 5) >>> kleinAE, kleinBE = Arc('A-2->E', A, E, 2), Arc('B-2->E', B, E, 2) >>> kleinDB, kleinFC = Arc('D-2->B', D, B, 2), Arc('F-3->C', F, C, 3) >>> G3 = Graphe('graphe3') >>> G3.setNoeuds([A, B, C, D, E, F]) >>> G3.setArcs([kleinAB, kleinBC, kleinCD, kleinDE, kleinEF]) >>> G3.addArcs([kleinFA, kleinAE, kleinBE, kleinDB, kleinFC]) >>> G3.addArcsInversesNuls() >>> cycleOK = [kleinAB, kleinBC, kleinCD, kleinDE, kleinEF, kleinFA] >>> for arc in cycleOK: ... print(arc.getNom()) ... A-4->B B-6->C C-5->D D-4->E E-7->F F-5->A >>> G3.kleinModifierCycle(cycleOK) >>> for arc in cycleOK: ... print(arc.getNom()) ... A-0->B B-2->C C-1->D D-0->E E-3->F F-1->A """ # noqa : E501 pass assert self.kleinEstCycle(arcsCycle), "arcsCycle doit être un cycle de Klein du Graphe." # noqa : E501 for arc in arcsCycle: assert isinstance(arc, Arc), "Tous les éléments de arcsCycle doivent être des instances de la classe Arc." # noqa : E501 minPoids = self.getMinPoidsFromCycle(arcsCycle) for arc in arcsCycle: # On diminue le poids de l'arc arc.setPoids(arc.getPoids()-minPoids) # On augmente le poids de son arc inverse arcInverse = self.getArcInverse(arc) arcInverse.setPoids(arcInverse.getPoids()+minPoids) # On renomme les arcs nouveauNom = arc.getNoeudDebut().getNom()+'-'+str(arc.getPoids())+'->'+arc.getNoeudFin().getNom() # noqa : E501 arc.setNom(nouveauNom) nouveauNomInverse = arcInverse.getNoeudDebut().getNom()+'-'+str(arcInverse.getPoids())+'->'+arcInverse.getNoeudFin().getNom() # noqa : E501 arcInverse.setNom(nouveauNomInverse) def kleinModifierCycleFromNoeud(self, noeudCandidat: Noeud) -> None: """Modifie un cycle de Klein pour un noeud du graphe s'il en existe, sinon le graphe n'est pas modifié. - Chaque arc dans le sens du cycle est modifié en diminuant son poids du plus petit poids des arcs du cycle. - Chaque arc dans le sens inverse du cycle est aussi modifié en augmentant son poids du plus petit poids des arcs du cycle. Renvoie l'instance du graphe modifiée ou pas selon l'existence d'un cycle de Klein. Paramètres ---------- noeudCandidat : Noeud - Une instance de la classe Noeud. Remarque -------- - Un cycle de Klein est un cycle : - dont tous les arcs ont un poids non nul. - dont le poids algébrique est strictement positif. - Le poids algébrique est obtenu en ajoutant le poids des arcs directs et en enlevant le poids des arcs inverses. Tests ----- >>> g = Graphe('graphe') >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3) >>> BC5 = Arc('B-5->C',B,C,5) >>> CA8,CA1 = Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1) >>> g.setNoeuds([A,B,C]) >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) >>> # On simplifie le graphe >>> g.simplification() >>> for arc in g.getArcs(): ... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse()) ... A-7->B => arc inverse : False A-3->C => arc inverse : False B-5->C => arc inverse : False C-9->A => arc inverse : False >>> # On ajoute les arcs inverses >>> g.addArcsInversesNuls() >>> for arc in g.getArcs(): ... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse()) ... A-7->B => arc inverse : False A-3->C => arc inverse : False B-5->C => arc inverse : False C-9->A => arc inverse : False B-0->A => arc inverse : True C-0->A => arc inverse : True C-0->B => arc inverse : True A-0->C => arc inverse : True >>> # Ce graphe présente deux cycles : ABCA et ACA >>> # On modifie ACA >>> random.seed(11) >>> g.kleinModifierCycleFromNoeud(A) >>> for arc in g.getArcs(): ... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse()) ... A-7->B => arc inverse : False A-0->C => arc inverse : False B-5->C => arc inverse : False C-6->A => arc inverse : False B-0->A => arc inverse : True C-3->A => arc inverse : True C-0->B => arc inverse : True A-3->C => arc inverse : True >>> # Il ne reste qu'un cycle ABCA, ACA est exclu car constitué par un arc et son propre inverse >>> for cycle in g.kleinAllArcsCyclesFromNoeud(A): ... print("=== cycle ===") ... for arc in cycle: ... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse()) ... === cycle === A-7->B => arc inverse : False B-5->C => arc inverse : False C-6->A => arc inverse : False === cycle === A-7->B => arc inverse : False B-5->C => arc inverse : False C-3->A => arc inverse : True """ # noqa : E501 pass assert isinstance(noeudCandidat, Noeud), "Le noeudCandidat doit être un instance de la classe Noeud." # noqa : E501 # Pour chaque arc du cycle, il doit y avoir un arc inverse for arc in self.getArcs(): assert self.hasArcInverse(arc), "Tous les arcs du graphe doivent avoir un arc inverse" # noqa : E501 if self.kleinHasCycleFromNoeud(noeudCandidat): # On choisit un cycle à réduire au hasard cycleToChange = random.choice( self.kleinAllArcsCyclesFromNoeud(noeudCandidat) ) self.kleinModifierCycle(cycleToChange) def kleinDeleteDettesNulles(self) -> None: """Supprime tous les arcs de poids nul. Tests ----- >>> g = Graphe('graphe') >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3) >>> BC5 = Arc('B-5->C',B,C,5) >>> CA8,CA1 = Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1) >>> g.setNoeuds([A,B,C]) >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) >>> # On simplifie le graphe >>> g.simplification() >>> for arc in g.getArcs(): ... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse()) ... A-7->B => arc inverse : False A-3->C => arc inverse : False B-5->C => arc inverse : False C-9->A => arc inverse : False >>> # On ajoute les arcs inverses >>> g.addArcsInversesNuls() >>> random.seed(1) >>> # Ce graphe présente deux cycles : ABCA et ACA >>> # On modifie ABCA >>> g.kleinModifierCycleFromNoeud(A) >>> for arc in g.getArcs(): ... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse()) ... A-2->B => arc inverse : False A-3->C => arc inverse : False B-0->C => arc inverse : False C-4->A => arc inverse : False B-5->A => arc inverse : True C-0->A => arc inverse : True C-5->B => arc inverse : True A-5->C => arc inverse : True >>> # On supprime les arcs de poids nul >>> g.kleinDeleteDettesNulles() >>> for arc in g.getArcs(): ... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse()) ... A-2->B => arc inverse : False A-3->C => arc inverse : False C-4->A => arc inverse : False B-5->A => arc inverse : True C-5->B => arc inverse : True A-5->C => arc inverse : True """ # noqa : E501 pass # On récupère d'abord tous les arcs à supprimer # Car la suppression de proche en proche peut laisser # un arc à l'écart à cause de la modification du graphe arcsToDelete = [] for arc in self.getArcs(): if arc.getPoids() == 0: arcsToDelete.append(arc) # On supprime alors tous les arcs à supprimer for arc in arcsToDelete: self.removeArc(arc) def kleinDeleteArcsInverses(self) -> None: """Supprime tous les arcs inverses temporaires. Tests ----- >>> g = Graphe('graphe') >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3) >>> BC5 = Arc('B-5->C',B,C,5) >>> CA8,CA1 = Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1) >>> g.setNoeuds([A,B,C]) >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) >>> # On simplifie le graphe >>> g.simplification() >>> for arc in g.getArcs(): ... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse()) ... A-7->B => arc inverse : False A-3->C => arc inverse : False B-5->C => arc inverse : False C-9->A => arc inverse : False >>> # On ajoute les arcs inverses >>> g.addArcsInversesNuls() >>> for arc in g.getArcs(): ... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse()) ... A-7->B => arc inverse : False A-3->C => arc inverse : False B-5->C => arc inverse : False C-9->A => arc inverse : False B-0->A => arc inverse : True C-0->A => arc inverse : True C-0->B => arc inverse : True A-0->C => arc inverse : True >>> random.seed(1) >>> # Ce graphe présente deux cycles : ABCA et ACA >>> # On modifie ABCA >>> g.kleinModifierCycleFromNoeud(A) >>> for arc in g.getArcs(): ... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse()) ... A-2->B => arc inverse : False A-3->C => arc inverse : False B-0->C => arc inverse : False C-4->A => arc inverse : False B-5->A => arc inverse : True C-0->A => arc inverse : True C-5->B => arc inverse : True A-5->C => arc inverse : True >>> # On supprime les arcs de poids nul >>> g.kleinDeleteDettesNulles() >>> for arc in g.getArcs(): ... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse()) ... A-2->B => arc inverse : False A-3->C => arc inverse : False C-4->A => arc inverse : False B-5->A => arc inverse : True C-5->B => arc inverse : True A-5->C => arc inverse : True >>> # On supprime les arcs inverses temporaires >>> g.kleinDeleteArcsInverses() >>> for arc in g.getArcs(): ... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse()) ... A-2->B => arc inverse : False A-3->C => arc inverse : False C-4->A => arc inverse : False """ # noqa : E501 pass # On récupère d'abord tous les arcs à supprimer # Car la suppression de proche en proche peut laisser # un arc à l'écart à cause de la modification du graphe arcsToDelete = [] for arc in self.getArcs(): if arc.getEstInverse(): arcsToDelete.append(arc) # On supprime alors tous les arcs à supprimer du graphe for arc in arcsToDelete: self.removeArc(arc) # ================================================================ # =========== METHODES NON UTILISÉES ============================= # ================================================================ def getNodesCycleFromNoeud(self, noeudDepart: Noeud) -> list: """Renvoie une liste de noeuds représentant un cycle partant du noeudDepart s'il en existe. [] sinon. Paramètres ---------- noeudDepart : Noeud - Le noeud de départ du cycle. Remarque -------- S'il existe plusieurs cycles à partir d'un noeud, l'algorithme n'en renvoie qu'un seul choisi au hasard. Tests ----- >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E') >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1) >>> BA = Arc('B->A',B,A,1) >>> CB = Arc('C->B',C,B,1) >>> DE = Arc('D->E',D,E,2) >>> EA = Arc('E->A',E,A,3) >>> g = Graphe('graphe') >>> g.setNoeuds([A,B,C,D,E]) >>> g.setArcs([AB,AC,AD,CB,DE,EA]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'A->D', 'C->B', 'D->E', 'E->A']) >>> for e in g.getNodesCycleFromNoeud(A): ... print(e.getNom()) ... A D E A >>> g.getNodesCycleFromNoeud(B) [] >>> g1 = Graphe('graphe1') >>> g1.setNoeuds([A,B,C,D,E]) >>> g1.setArcs([AB,AC,BA,CB,DE,EA]) >>> print(g1) Graphe(nom=graphe1, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'B->A', 'C->B', 'D->E', 'E->A']) >>> # Il y a deux cycles partants de A dans ce graphe >>> random.seed(3) >>> for e in g1.getNodesCycleFromNoeud(A): ... print(e.getNom()) ... A B A >>> random.seed(7) >>> for e in g1.getNodesCycleFromNoeud(A): ... print(e.getNom()) ... A C B A """ # noqa : E501 pass assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud." # noqa : E501 cycle = [] if self.hasCycleFromNoeud(noeudDepart): cycle.append(noeudDepart) noeudsPossibles = [] for noeud in self.getCreanciers(noeudDepart): if self.getNodesWays(noeud, noeudDepart): noeudsPossibles.append(noeud) noeudAleatoire = random.choice(noeudsPossibles) cheminsPossibles = self.getNodesWays(noeudAleatoire, noeudDepart) cheminAleatoire = random.choice(cheminsPossibles) cycle.extend(cheminAleatoire) return cycle def kleinBestArcsCycle(self) -> list: """Renvoie le liste d'arcs réduisant au maximum la dette globale d'un graphe. Remarques --------- - Il semble qu'en choisissant à chaque étape le cycle qui réduit au maximum la dette globale, la réduction finale ne soit pas la meilleure. - Cette fonction est donc incorrecte Tests ----- >>> A, B, C = Noeud('A'), Noeud('B'), Noeud('C') >>> D, E, F = Noeud('D'), Noeud('E'), Noeud('F') >>> kleinAB, kleinBC = Arc('A-4->B', A, B, 4), Arc('B-6->C', B, C, 6) >>> kleinCD, kleinDE = Arc('C-5->D', C, D, 5), Arc('D-4->E', D, E, 4) >>> kleinEF, kleinFA = Arc('E-7->F', E, F, 7), Arc('F-5->A', F, A, 5) >>> kleinAE, kleinBE = Arc('A-2->E', A, E, 2), Arc('B-2->E', B, E, 2) >>> kleinDB, kleinFC = Arc('D-2->B', D, B, 2), Arc('F-3->C', F, C, 3) >>> G3 = Graphe('graphe3') >>> G3.setNoeuds([A, B, C, D, E, F]) >>> G3.setArcs([kleinAB, kleinBC, kleinCD, kleinDE, kleinEF]) >>> G3.addArcs([kleinFA, kleinAE, kleinBE, kleinDB, kleinFC]) >>> # Ce graphe compte 25 cycles si on n'élimine pas les doublons >>> # Mais 6 cycles distincts >>> print(len(G3.kleinAllArcsCycles())) 6 >>> G3.addArcsInversesNuls() >>> bestCycle1 = G3.kleinBestArcsCycle() >>> for arc in bestCycle1: ... print(arc.getNom()) ... A-4->B B-6->C C-5->D D-4->E E-7->F F-5->A >>> G3.kleinModifierCycle(bestCycle1) >>> bestCycle2 = G3.kleinBestArcsCycle() >>> for arc in bestCycle2: ... print(arc.getNom()) ... B-2->E E-3->F F-3->C C-4->B """ pass # On récupère tous les cycles sans doublons allCycles = self.kleinAllArcsCycles() bestCycle = [] reductionDeLaDette = 0 for cycle in allCycles: reductionDuCycle = len(cycle)*self.getMinPoidsFromCycle(cycle) if reductionDuCycle > reductionDeLaDette: reductionDeLaDette = reductionDuCycle bestCycle = [] bestCycle.extend(cycle) return bestCycle def colorierGrapheFromNoeud(self, noeudDepart: Noeud) -> tuple: """Renvoie un tuple contenant deux dictionnaires issu d'un parcours en profondeur : - 1er : Clef : nom d'un noeud - Valeur : père du noeud dans le parcours choisi - 2e : Clef : nom d'un noeud - Valeur : couleur du noeud blanc : non visité gris : en cours de visite noir : visité Remarques --------- - J'ai codé cette fonction à un moment dans mes errances mais je ne suis pas sûr de m'en servir. Toutefois je ne veux pas perdre le code pour le moment. - La méthode ne s'occupe que de faire les coloriages à partir de noeudDepart, il faudrait la rappeler recursivement pour qu'elle traite tout le graphe. - L'algorithme du parcours n'est ni aléatoire ni exhaustif, il ne traite pas tous les cas possibles. - À partir du noeudDepart, on va sur un sommet blanc tant que c'est possible, on le colorie en gris tant que le traitement n'est pas fini c'est à dire qu'on ne dépile pas. Quand il n'y a plus de noeud blanc accessible on dépile. Tests ----- >>> A,B,C,D = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D') >>> E,F,G,H = Noeud('E'),Noeud('F'),Noeud('G'),Noeud('H') >>> AC,AD = Arc('A->C',A,C,1),Arc('A->D',A,D,1) >>> BD,BF = Arc('B->D',B,D,1),Arc('B->F',B,F,1) >>> CF,CB,CD = Arc('C->F',C,F,1),Arc('C->B',C,B,1),Arc('C->D',C,D,1) >>> DE,DG = Arc('D->E',D,E,1),Arc('D->G',D,G,1) >>> FH = Arc('F->H',F,H,1) >>> GE = Arc('G->E',G,E,1) >>> HB = Arc('H->B',H,B,1) >>> g = Graphe('graphe') >>> g.setNoeuds([A,B,C,D,E,F,G,H]) >>> g.setArcs([AC,AD,BD,BF,CF,CB,CD,DE,DG,FH,GE,HB]) >>> # Dictionnaire des pères à partir de D >>> g.colorierGrapheFromNoeud(D)[0] {'D': None, 'E': 'D', 'G': 'D'} >>> # Dictionnaire du coloriage partir de D >>> g.colorierGrapheFromNoeud(D)[1] {'A': 'blanc', 'B': 'blanc', 'C': 'blanc', 'D': 'noir', 'E': 'noir', 'F': 'blanc', 'G': 'noir', 'H': 'blanc'} >>> # Dictionnaire des pères à partir de C >>> g.colorierGrapheFromNoeud(C)[0] {'C': None, 'F': 'C', 'H': 'F', 'B': 'H', 'D': 'B', 'E': 'D', 'G': 'D'} >>> # Dictionnaire du coloriage partir de C >>> g.colorierGrapheFromNoeud(C)[1] {'A': 'blanc', 'B': 'noir', 'C': 'noir', 'D': 'noir', 'E': 'noir', 'F': 'noir', 'G': 'noir', 'H': 'noir'} """ # noqa : E501 pass assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud." # noqa : E501 pile = [] parents = {} couleurs = {} for noeud in self.getNoeuds(): couleurs[noeud.getNom()] = 'blanc' parents[noeudDepart.getNom()] = None couleurs[noeudDepart.getNom()] = 'gris' pile.append(noeudDepart) while pile != []: pere = pile[-1] descendants = [] for noeud in self.getCreanciers(pere): if couleurs[noeud.getNom()] == 'blanc': descendants.append(noeud) if descendants != []: fils = descendants[0] couleurs[fils.getNom()] = 'gris' parents[fils.getNom()] = pere.getNom() pile.append(fils) else: pile.pop() couleurs[pere.getNom()] = 'noir' return parents, couleurs
Methods
def addArc(self, arcCandidat: models.arc.Arc) ‑> None
-
Ajoute une instance de la classe Arc au graphe.
Paramètres
arcCandidat : Arc - Un arc à ajouter au graphe.
Tests
>>> g1 = Graphe('graphe1') >>> print(g1) Graphe(nom=graphe1, noeuds=[], arcs=[]) >>> n1 = Noeud('n1') >>> n2 = Noeud('n2') >>> g1.addNoeud(n1) >>> g1.addNoeud(n2) >>> print(g1) Graphe(nom=graphe1, noeuds=['n1', 'n2'], arcs=[]) >>> arc = Arc('n1n2', n1, n2, 7) >>> g1.addArc(arc) >>> print(g1) Graphe(nom=graphe1, noeuds=['n1', 'n2'], arcs=['n1n2'])
Expand source code
def addArc(self, arcCandidat: Arc) -> None: """Ajoute une instance de la classe Arc au graphe. Paramètres ---------- arcCandidat : Arc - Un arc à ajouter au graphe. Tests ----- >>> g1 = Graphe('graphe1') >>> print(g1) Graphe(nom=graphe1, noeuds=[], arcs=[]) >>> n1 = Noeud('n1') >>> n2 = Noeud('n2') >>> g1.addNoeud(n1) >>> g1.addNoeud(n2) >>> print(g1) Graphe(nom=graphe1, noeuds=['n1', 'n2'], arcs=[]) >>> arc = Arc('n1n2', n1, n2, 7) >>> g1.addArc(arc) >>> print(g1) Graphe(nom=graphe1, noeuds=['n1', 'n2'], arcs=['n1n2']) """ pass assert isinstance(arcCandidat, Arc), "arc doit être une instance de la classe Arc." # noqa : E501 assert arcCandidat not in self.arcs, "Pas de doublon pour les arcs d'une instance de Graphe." # noqa : E501 nomsDesArcsDuGraphe = [] for arc in self.getArcs(): nomsDesArcsDuGraphe.append(arc.nom) assert arcCandidat.nom not in nomsDesArcsDuGraphe,"Deux arcs d'une instance de Graphe ne peuvent avoir le même nom." # noqa : E501 # Un booléen pour tester si tout est OK allOk = True # Ne pas utiliser les assertions pour valider les données # car elles peuvent être désactivées. # On lève donc une erreur en cas de souci if arcCandidat in self.getArcs(): allOk = False raise ValueError('Pas de doublon pour les arcs d\'une instance de Graphe.') # noqa : E501 if arcCandidat.nom in nomsDesArcsDuGraphe: allOk = False raise ValueError('Deux arcs d\'une instance de Graphe ne peuvent avoir le même nom.') # noqa : E501 if allOk: self.arcs.append(arcCandidat)
def addArcs(self, arcsToAdd: list) ‑> None
-
Ajoute une liste d'instances de la classe Arc au graphe.
Paramètres
arcsToAdd : list - Une liste d'arcs à ajouter au graphe.
Tests
>>> g = Graphe('graphe') >>> print(g) Graphe(nom=graphe, noeuds=[], arcs=[]) >>> A,B = Noeud('A'), Noeud('B') >>> g.setNoeuds([A,B]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B'], arcs=[]) >>> AB1 = Arc('A->B(1)', A, B, 7) >>> g.addArc(AB1) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B'], arcs=['A->B(1)']) >>> AB2,AB3 = Arc('A->B(2)', A, B, 7),Arc('A->B(3)', A, B, 7) >>> g.addArcs([AB2,AB3]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B'], arcs=['A->B(1)', 'A->B(2)', 'A->B(3)'])
Expand source code
def addArcs(self, arcsToAdd: list) -> None: """Ajoute une liste d'instances de la classe Arc au graphe. Paramètres ---------- arcsToAdd : list - Une liste d'arcs à ajouter au graphe. Tests ----- >>> g = Graphe('graphe') >>> print(g) Graphe(nom=graphe, noeuds=[], arcs=[]) >>> A,B = Noeud('A'), Noeud('B') >>> g.setNoeuds([A,B]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B'], arcs=[]) >>> AB1 = Arc('A->B(1)', A, B, 7) >>> g.addArc(AB1) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B'], arcs=['A->B(1)']) >>> AB2,AB3 = Arc('A->B(2)', A, B, 7),Arc('A->B(3)', A, B, 7) >>> g.addArcs([AB2,AB3]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B'], arcs=['A->B(1)', 'A->B(2)', 'A->B(3)']) """ # noqa : E501 pass for arc in arcsToAdd: assert isinstance(arc, Arc), "arc doit être une instance de la classe Arc." # noqa : E501 for arc in arcsToAdd: self.addArc(arc)
def addArcsInversesNuls(self) ‑> None
-
Ajoute un arc inverse de valeur 0 pour chaque arc du graphe.
Remarque
Si le graphe n'est pas simplifié, on le simplifie d'abord.
Tests
>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3) >>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1) >>> g = Graphe('graphe') >>> g.setNoeuds([A,B,C]) >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A']) >>> for arc in g.getArcs(): ... print(arc) ... Arc(nom=A-5->B, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), noeudFin=Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=0), poids=5, estInverse=False) Arc(nom=A-2->B, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), noeudFin=Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=0), poids=2, estInverse=False) Arc(nom=A-3->C, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), noeudFin=Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0), poids=3, estInverse=False) Arc(nom=B-5->C, noeudDebut=Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=0), noeudFin=Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0), poids=5, estInverse=False) Arc(nom=C-8->A, noeudDebut=Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0), noeudFin=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), poids=8, estInverse=False) Arc(nom=C-1->A, noeudDebut=Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0), noeudFin=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), poids=1, estInverse=False) >>> g.addArcsInversesNuls() >>> for arc in g.getArcs(): ... print(arc) ... Arc(nom=A-7->B, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), noeudFin=Noeud(nom=B, voisins=['A', 'C'], nbArcs=4, capital=0), poids=7, estInverse=False) Arc(nom=A-3->C, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), noeudFin=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), poids=3, estInverse=False) Arc(nom=B-5->C, noeudDebut=Noeud(nom=B, voisins=['A', 'C'], nbArcs=4, capital=0), noeudFin=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), poids=5, estInverse=False) Arc(nom=C-9->A, noeudDebut=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), noeudFin=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), poids=9, estInverse=False) Arc(nom=B-0->A, noeudDebut=Noeud(nom=B, voisins=['A', 'C'], nbArcs=4, capital=0), noeudFin=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), poids=0, estInverse=True) Arc(nom=C-0->A, noeudDebut=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), noeudFin=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), poids=0, estInverse=True) Arc(nom=C-0->B, noeudDebut=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), noeudFin=Noeud(nom=B, voisins=['A', 'C'], nbArcs=4, capital=0), poids=0, estInverse=True) Arc(nom=A-0->C, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), noeudFin=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), poids=0, estInverse=True)
Expand source code
def addArcsInversesNuls(self) -> None: """Ajoute un arc inverse de valeur 0 pour chaque arc du graphe. Remarque -------- Si le graphe n'est pas simplifié, on le simplifie d'abord. Tests ----- >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3) >>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1) >>> g = Graphe('graphe') >>> g.setNoeuds([A,B,C]) >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A']) >>> for arc in g.getArcs(): ... print(arc) ... Arc(nom=A-5->B, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), noeudFin=Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=0), poids=5, estInverse=False) Arc(nom=A-2->B, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), noeudFin=Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=0), poids=2, estInverse=False) Arc(nom=A-3->C, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), noeudFin=Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0), poids=3, estInverse=False) Arc(nom=B-5->C, noeudDebut=Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=0), noeudFin=Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0), poids=5, estInverse=False) Arc(nom=C-8->A, noeudDebut=Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0), noeudFin=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), poids=8, estInverse=False) Arc(nom=C-1->A, noeudDebut=Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0), noeudFin=Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0), poids=1, estInverse=False) >>> g.addArcsInversesNuls() >>> for arc in g.getArcs(): ... print(arc) ... Arc(nom=A-7->B, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), noeudFin=Noeud(nom=B, voisins=['A', 'C'], nbArcs=4, capital=0), poids=7, estInverse=False) Arc(nom=A-3->C, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), noeudFin=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), poids=3, estInverse=False) Arc(nom=B-5->C, noeudDebut=Noeud(nom=B, voisins=['A', 'C'], nbArcs=4, capital=0), noeudFin=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), poids=5, estInverse=False) Arc(nom=C-9->A, noeudDebut=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), noeudFin=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), poids=9, estInverse=False) Arc(nom=B-0->A, noeudDebut=Noeud(nom=B, voisins=['A', 'C'], nbArcs=4, capital=0), noeudFin=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), poids=0, estInverse=True) Arc(nom=C-0->A, noeudDebut=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), noeudFin=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), poids=0, estInverse=True) Arc(nom=C-0->B, noeudDebut=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), noeudFin=Noeud(nom=B, voisins=['A', 'C'], nbArcs=4, capital=0), poids=0, estInverse=True) Arc(nom=A-0->C, noeudDebut=Noeud(nom=A, voisins=['B', 'C'], nbArcs=6, capital=0), noeudFin=Noeud(nom=C, voisins=['A', 'B'], nbArcs=6, capital=0), poids=0, estInverse=True) """ # noqa : E501 pass assert self.hasArc(), "Le graphe doit avoir des arcs." if not self.estSimplifie(): self.simplification() newArcs = [] for arc in self.getArcs(): newName = "{}-0->{}".format( arc.getNoeudFin().getNom(), arc.getNoeudDebut().getNom() ) newArcInverseNul = Arc( newName, arc.getNoeudFin(), arc.getNoeudDebut(), 0, True ) newArcs.append(newArcInverseNul) for arc in newArcs: self.addArc(arc)
def addNoeud(self, noeudCandidat: models.noeud.Noeud) ‑> None
-
Ajoute une instance de la classe Noeud au graphe.
Paramètres
noeudCandidat : Noeud - Un noeud à ajouter au graphe.
Tests
>>> g1 = Graphe('graphe1') >>> print(g1) Graphe(nom=graphe1, noeuds=[], arcs=[]) >>> n1 = Noeud('n1') >>> g1.addNoeud(n1) >>> print(g1) Graphe(nom=graphe1, noeuds=['n1'], arcs=[])
Expand source code
def addNoeud(self, noeudCandidat: Noeud) -> None: """Ajoute une instance de la classe Noeud au graphe. Paramètres ---------- noeudCandidat : Noeud - Un noeud à ajouter au graphe. Tests ----- >>> g1 = Graphe('graphe1') >>> print(g1) Graphe(nom=graphe1, noeuds=[], arcs=[]) >>> n1 = Noeud('n1') >>> g1.addNoeud(n1) >>> print(g1) Graphe(nom=graphe1, noeuds=['n1'], arcs=[]) """ pass assert isinstance(noeudCandidat, Noeud), "noeudCandidat doit être une instance de la classe Noeud." # noqa : E501 assert noeudCandidat not in self.noeuds, "Pas de doublon pour les noeuds d'une instance de Graphe." # noqa : E501 nomsDesNoeudsDuGraphe = [] for noeud in self.noeuds: nomsDesNoeudsDuGraphe.append(noeud.nom) assert noeudCandidat.nom not in nomsDesNoeudsDuGraphe, "Deux noeuds d'une instance de Graphe ne peuvent avoir le même nom." # noqa : E501 # Un booléen pour tester si tout est OK allOk = True # Ne pas utiliser les assertions pour valider les données # car elles peuvent être désactivées. # On lève donc une erreur en cas de souci if self.isNoeudInGraphe(noeudCandidat): allOk = False raise ValueError('Pas de doublon pour les noeuds d\'une instance de Graphe.') # noqa : E501 if noeudCandidat.nom in nomsDesNoeudsDuGraphe: allOk = False raise ValueError('Deux noeuds d\'une instance de Graphe ne peuvent avoir le même nom.') # noqa : E501 if allOk: self.noeuds.append(noeudCandidat)
def colorierGrapheFromNoeud(self, noeudDepart: models.noeud.Noeud) ‑> tuple
-
Renvoie un tuple contenant deux dictionnaires issu d'un parcours en profondeur : - 1er : Clef : nom d'un noeud - Valeur : père du noeud dans le parcours choisi - 2e : Clef : nom d'un noeud - Valeur : couleur du noeud
blanc : non visité gris : en cours de visite noir : visité
Remarques
- J'ai codé cette fonction à un moment dans mes errances mais je ne suis pas sûr de m'en servir. Toutefois je ne veux pas perdre le code pour le moment.
- La méthode ne s'occupe que de faire les coloriages à partir de noeudDepart, il faudrait la rappeler recursivement pour qu'elle traite tout le graphe.
- L'algorithme du parcours n'est ni aléatoire ni exhaustif, il ne traite pas tous les cas possibles.
- À partir du noeudDepart, on va sur un sommet blanc tant que c'est possible, on le colorie en gris tant que le traitement n'est pas fini c'est à dire qu'on ne dépile pas. Quand il n'y a plus de noeud blanc accessible on dépile.
Tests
>>> A,B,C,D = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D') >>> E,F,G,H = Noeud('E'),Noeud('F'),Noeud('G'),Noeud('H') >>> AC,AD = Arc('A->C',A,C,1),Arc('A->D',A,D,1) >>> BD,BF = Arc('B->D',B,D,1),Arc('B->F',B,F,1) >>> CF,CB,CD = Arc('C->F',C,F,1),Arc('C->B',C,B,1),Arc('C->D',C,D,1) >>> DE,DG = Arc('D->E',D,E,1),Arc('D->G',D,G,1) >>> FH = Arc('F->H',F,H,1) >>> GE = Arc('G->E',G,E,1) >>> HB = Arc('H->B',H,B,1) >>> g = Graphe('graphe') >>> g.setNoeuds([A,B,C,D,E,F,G,H]) >>> g.setArcs([AC,AD,BD,BF,CF,CB,CD,DE,DG,FH,GE,HB]) >>> # Dictionnaire des pères à partir de D >>> g.colorierGrapheFromNoeud(D)[0] {'D': None, 'E': 'D', 'G': 'D'} >>> # Dictionnaire du coloriage partir de D >>> g.colorierGrapheFromNoeud(D)[1] {'A': 'blanc', 'B': 'blanc', 'C': 'blanc', 'D': 'noir', 'E': 'noir', 'F': 'blanc', 'G': 'noir', 'H': 'blanc'} >>> # Dictionnaire des pères à partir de C >>> g.colorierGrapheFromNoeud(C)[0] {'C': None, 'F': 'C', 'H': 'F', 'B': 'H', 'D': 'B', 'E': 'D', 'G': 'D'} >>> # Dictionnaire du coloriage partir de C >>> g.colorierGrapheFromNoeud(C)[1] {'A': 'blanc', 'B': 'noir', 'C': 'noir', 'D': 'noir', 'E': 'noir', 'F': 'noir', 'G': 'noir', 'H': 'noir'}
Expand source code
def colorierGrapheFromNoeud(self, noeudDepart: Noeud) -> tuple: """Renvoie un tuple contenant deux dictionnaires issu d'un parcours en profondeur : - 1er : Clef : nom d'un noeud - Valeur : père du noeud dans le parcours choisi - 2e : Clef : nom d'un noeud - Valeur : couleur du noeud blanc : non visité gris : en cours de visite noir : visité Remarques --------- - J'ai codé cette fonction à un moment dans mes errances mais je ne suis pas sûr de m'en servir. Toutefois je ne veux pas perdre le code pour le moment. - La méthode ne s'occupe que de faire les coloriages à partir de noeudDepart, il faudrait la rappeler recursivement pour qu'elle traite tout le graphe. - L'algorithme du parcours n'est ni aléatoire ni exhaustif, il ne traite pas tous les cas possibles. - À partir du noeudDepart, on va sur un sommet blanc tant que c'est possible, on le colorie en gris tant que le traitement n'est pas fini c'est à dire qu'on ne dépile pas. Quand il n'y a plus de noeud blanc accessible on dépile. Tests ----- >>> A,B,C,D = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D') >>> E,F,G,H = Noeud('E'),Noeud('F'),Noeud('G'),Noeud('H') >>> AC,AD = Arc('A->C',A,C,1),Arc('A->D',A,D,1) >>> BD,BF = Arc('B->D',B,D,1),Arc('B->F',B,F,1) >>> CF,CB,CD = Arc('C->F',C,F,1),Arc('C->B',C,B,1),Arc('C->D',C,D,1) >>> DE,DG = Arc('D->E',D,E,1),Arc('D->G',D,G,1) >>> FH = Arc('F->H',F,H,1) >>> GE = Arc('G->E',G,E,1) >>> HB = Arc('H->B',H,B,1) >>> g = Graphe('graphe') >>> g.setNoeuds([A,B,C,D,E,F,G,H]) >>> g.setArcs([AC,AD,BD,BF,CF,CB,CD,DE,DG,FH,GE,HB]) >>> # Dictionnaire des pères à partir de D >>> g.colorierGrapheFromNoeud(D)[0] {'D': None, 'E': 'D', 'G': 'D'} >>> # Dictionnaire du coloriage partir de D >>> g.colorierGrapheFromNoeud(D)[1] {'A': 'blanc', 'B': 'blanc', 'C': 'blanc', 'D': 'noir', 'E': 'noir', 'F': 'blanc', 'G': 'noir', 'H': 'blanc'} >>> # Dictionnaire des pères à partir de C >>> g.colorierGrapheFromNoeud(C)[0] {'C': None, 'F': 'C', 'H': 'F', 'B': 'H', 'D': 'B', 'E': 'D', 'G': 'D'} >>> # Dictionnaire du coloriage partir de C >>> g.colorierGrapheFromNoeud(C)[1] {'A': 'blanc', 'B': 'noir', 'C': 'noir', 'D': 'noir', 'E': 'noir', 'F': 'noir', 'G': 'noir', 'H': 'noir'} """ # noqa : E501 pass assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud." # noqa : E501 pile = [] parents = {} couleurs = {} for noeud in self.getNoeuds(): couleurs[noeud.getNom()] = 'blanc' parents[noeudDepart.getNom()] = None couleurs[noeudDepart.getNom()] = 'gris' pile.append(noeudDepart) while pile != []: pere = pile[-1] descendants = [] for noeud in self.getCreanciers(pere): if couleurs[noeud.getNom()] == 'blanc': descendants.append(noeud) if descendants != []: fils = descendants[0] couleurs[fils.getNom()] = 'gris' parents[fils.getNom()] = pere.getNom() pile.append(fils) else: pile.pop() couleurs[pere.getNom()] = 'noir' return parents, couleurs
def detteGlobale(self) ‑> int
-
Retourne la dette globale du graphe, c'est à dire la somme des dettes des entreprises.
Tests
>>> g1 = Graphe('graphe1') >>> E,D,F = Noeud('E'),Noeud('D'),Noeud('F') >>> ED,DF,FE = Arc('ED',E,D,10),Arc('DF',D,F,7),Arc('FE',F,E,2) >>> g1.setNoeuds([E,D,F]) >>> g1.setArcs([ED,DF,FE]) >>> g1.detteGlobale() 19 >>> g2 = Graphe('graphe2') >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> # arcs partant de A >>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3) >>> # arcs arrivant sur A >>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1) >>> # arc entre B et C >>> BC = Arc('BC',B,C,5) >>> g2.setNoeuds([A,B,C]) >>> g2.setArcs([AB1,AB2,AC,CA1,CA2,BC]) >>> g2.detteGlobale() 24
Expand source code
def detteGlobale(self) -> int: """Retourne la dette globale du graphe, c'est à dire la somme des dettes des entreprises. Tests ----- >>> g1 = Graphe('graphe1') >>> E,D,F = Noeud('E'),Noeud('D'),Noeud('F') >>> ED,DF,FE = Arc('ED',E,D,10),Arc('DF',D,F,7),Arc('FE',F,E,2) >>> g1.setNoeuds([E,D,F]) >>> g1.setArcs([ED,DF,FE]) >>> g1.detteGlobale() 19 >>> g2 = Graphe('graphe2') >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> # arcs partant de A >>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3) >>> # arcs arrivant sur A >>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1) >>> # arc entre B et C >>> BC = Arc('BC',B,C,5) >>> g2.setNoeuds([A,B,C]) >>> g2.setArcs([AB1,AB2,AC,CA1,CA2,BC]) >>> g2.detteGlobale() 24 """ # noqa : E501 pass assert len(self.getNoeuds()) >= 2, "Le graphe doit avoir au moins deux noeuds." # noqa : E501 # Ne pas utiliser les assertions pour valider les données # car elles peuvent être désactivées. # On lève donc une erreur en cas de souci allOk = True if len(self.getNoeuds()) < 2: allOk = False raise ValueError("Le graphe doit avoir au moins deux noeuds.") if allOk: detteGlobale = 0 for arc in self.getArcs(): detteGlobale += arc.poids return detteGlobale
def eliminerCycleFromNoeud(self, noeudDepart: models.noeud.Noeud) ‑> None
-
Renvoie l'instance du graphe modifiée. Si le graphe contient un cycle à partir de noeudDepart, les arcs de ce cycle sont réduits du plus petit poids des arcs.
Sinon le graphe n'est pas modifié.
Paramètres
noeudDepart : Noeud - Le noeud de départ du cycle à éliminer.
Remarque
Lorsque le graphe présente un cycle à partir de noeudDepart, cela revient à le supprimer.
Tests
>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3) >>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1) >>> g = Graphe('graphe') >>> g.setNoeuds([A,B,C]) >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A']) >>> g.simplification() >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A']) >>> # Ce graphe contient 2 cycles à partir de A >>> for cycle in g.getAllNodesCyclesFromNoeud(A): ... print("====cycle====") ... for noeud in cycle: ... print(noeud.getNom()) ... ====cycle==== A B C A ====cycle==== A C A >>> # On élimine le premier, il reste le second >>> random.seed(7) >>> g.eliminerCycleFromNoeud(A) >>> for cycle in g.getAllNodesCyclesFromNoeud(A): ... print("====cycle====") ... for noeud in cycle: ... print(noeud.getNom()) ... ====cycle==== A C A >>> # On élimine le second >>> g.eliminerCycleFromNoeud(A) >>> # Il n'y a plus de cycle partant de A >>> for cycle in g.getAllNodesCyclesFromNoeud(A): ... print("====cycle====") ... for noeud in cycle: ... print(noeud.getNom()) ...
Expand source code
def eliminerCycleFromNoeud(self, noeudDepart: Noeud) -> None: """Renvoie l'instance du graphe modifiée. Si le graphe contient un cycle à partir de noeudDepart, les arcs de ce cycle sont réduits du plus petit poids des arcs. Sinon le graphe n'est pas modifié. Paramètres ---------- noeudDepart : Noeud - Le noeud de départ du cycle à éliminer. Remarque -------- Lorsque le graphe présente un cycle à partir de noeudDepart, cela revient à le supprimer. Tests ----- >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3) >>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1) >>> g = Graphe('graphe') >>> g.setNoeuds([A,B,C]) >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A']) >>> g.simplification() >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A']) >>> # Ce graphe contient 2 cycles à partir de A >>> for cycle in g.getAllNodesCyclesFromNoeud(A): ... print("====cycle====") ... for noeud in cycle: ... print(noeud.getNom()) ... ====cycle==== A B C A ====cycle==== A C A >>> # On élimine le premier, il reste le second >>> random.seed(7) >>> g.eliminerCycleFromNoeud(A) >>> for cycle in g.getAllNodesCyclesFromNoeud(A): ... print("====cycle====") ... for noeud in cycle: ... print(noeud.getNom()) ... ====cycle==== A C A >>> # On élimine le second >>> g.eliminerCycleFromNoeud(A) >>> # Il n'y a plus de cycle partant de A >>> for cycle in g.getAllNodesCyclesFromNoeud(A): ... print("====cycle====") ... for noeud in cycle: ... print(noeud.getNom()) ... """ # noqa : E501 pass assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud." # noqa : E501 if self.hasCycleFromNoeud(noeudDepart): cycleToReduce = self.getArcsCycleFromNoeud(noeudDepart) minPoids = self.getMinPoidsFromCycle(cycleToReduce) for arc in cycleToReduce: arc.setPoids(arc.getPoids()-minPoids) # On renomme l'arc nouveauNom = arc.getNoeudDebut().getNom()+'-'+str(arc.getPoids())+'->'+arc.getNoeudFin().getNom() # noqa : E501 arc.setNom(nouveauNom) if arc.getPoids() == 0: self.removeArc(arc)
def estSimplifie(self) ‑> bool
-
Retourne un booléen selon que le graphe est simplifié ou non.
Tests
>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3) >>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1) >>> g = Graphe('graphe') >>> g.setNoeuds([A,B,C]) >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A']) >>> g.estSimplifie() False >>> g1 = copy.deepcopy(g) >>> g1.simplification() >>> print(g1) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A']) >>> g1.estSimplifie() True >>> g2 = Graphe('graphe2') >>> g2.setNoeuds([A,B]) >>> AB1,BA1 = Arc('A-1->B(1)',A,B,1),Arc('B-1->A(1)',B,A,1) >>> g2.setArcs([AB1,BA1]) >>> print(g2) Graphe(nom=graphe2, noeuds=['A', 'B'], arcs=['A-1->B(1)', 'B-1->A(1)']) >>> g2.addArcsInversesNuls() >>> print(g2) Graphe(nom=graphe2, noeuds=['A', 'B'], arcs=['A-1->B(1)', 'B-1->A(1)', 'B-0->A', 'A-0->B']) >>> # la méthode estSimplifie() ne doit tenir compte que des arcs directs >>> g2.estSimplifie() True
Expand source code
def estSimplifie(self) -> bool: """Retourne un booléen selon que le graphe est simplifié ou non. Tests ----- >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3) >>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1) >>> g = Graphe('graphe') >>> g.setNoeuds([A,B,C]) >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A']) >>> g.estSimplifie() False >>> g1 = copy.deepcopy(g) >>> g1.simplification() >>> print(g1) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A']) >>> g1.estSimplifie() True >>> g2 = Graphe('graphe2') >>> g2.setNoeuds([A,B]) >>> AB1,BA1 = Arc('A-1->B(1)',A,B,1),Arc('B-1->A(1)',B,A,1) >>> g2.setArcs([AB1,BA1]) >>> print(g2) Graphe(nom=graphe2, noeuds=['A', 'B'], arcs=['A-1->B(1)', 'B-1->A(1)']) >>> g2.addArcsInversesNuls() >>> print(g2) Graphe(nom=graphe2, noeuds=['A', 'B'], arcs=['A-1->B(1)', 'B-1->A(1)', 'B-0->A', 'A-0->B']) >>> # la méthode estSimplifie() ne doit tenir compte que des arcs directs >>> g2.estSimplifie() True """ # noqa : E501 pass arcsDirects = self.getArcsDirects() estSimplifie = True for i in range(len(arcsDirects)): k = 0 for j in range(len(arcsDirects)): if (arcsDirects[i].getNoeudDebut() == arcsDirects[j].getNoeudDebut() and # noqa : E501 arcsDirects[i].getNoeudFin() == arcsDirects[j].getNoeudFin()): # noqa : E501 k += 1 if k > 1: estSimplifie = False return estSimplifie
def getAllNodesCyclesFromNoeud(self, noeudDepart: models.noeud.Noeud) ‑> list
-
Renvoie tous les cycles partant du noeudDepart s'il en existe.
Paramètres
noeudDepart : Noeud - Le noeud de départ des cycles.
Tests
>>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E') >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1) >>> BA = Arc('B->A',B,A,1) >>> CB = Arc('C->B',C,B,1) >>> DE = Arc('D->E',D,E,2) >>> EA = Arc('E->A',E,A,3) >>> g = Graphe('graphe') >>> g.setNoeuds([A,B,C,D,E]) >>> g.setArcs([AB,AC,AD,CB,DE,EA]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'A->D', 'C->B', 'D->E', 'E->A']) >>> for cycle in g.getAllNodesCyclesFromNoeud(A): ... print("====cycle====") ... for n in cycle: ... print(n.getNom()) ... ====cycle==== A D E A >>> g1 = Graphe('graphe1') >>> g1.setNoeuds([A,B,C,D,E]) >>> g1.setArcs([AB,AC,BA,CB,DE,EA]) >>> print(g1) Graphe(nom=graphe1, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'B->A', 'C->B', 'D->E', 'E->A']) >>> for cycle in g1.getAllNodesCyclesFromNoeud(A): ... print("====cycle====") ... for n in cycle: ... print(n.getNom()) ... ====cycle==== A B A ====cycle==== A C B A >>> A1,B1,C1 = Noeud('A'),Noeud('B'),Noeud('C') >>> A1B1,A1B1bis,A1C1 = Arc('A-5->B',A1,B1,5),Arc('A-2->B',A1,B1,2),Arc('A-3->C',A1,C1,3) >>> B1C1,C1A1,C1A1bis = Arc('B-5->C',B1,C1,5),Arc('C-8->A',C1,A1,8),Arc('C-1->A',C1,A1,1) >>> g2 = Graphe('graphe2') >>> g2.setNoeuds([A1,B1,C1]) >>> g2.setArcs([A1B1,A1B1bis,A1C1,B1C1,C1A1,C1A1bis]) >>> print(g2) Graphe(nom=graphe2, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A']) >>> # Il y a 6 cycles distincts partant de A1 >>> # On les affiche tous, ils semblent en doublon mais ce sont les arcs les liant qui diffèrent. >>> for cycle in g2.getAllNodesCyclesFromNoeud(A1): ... print("====cycle====") ... for n in cycle: ... print(n.getNom()) ... ====cycle==== A B C A ====cycle==== A B C A ====cycle==== A B C A ====cycle==== A B C A ====cycle==== A C A ====cycle==== A C A
Expand source code
def getAllNodesCyclesFromNoeud(self, noeudDepart: Noeud) -> list: """Renvoie tous les cycles partant du noeudDepart s'il en existe. Paramètres ---------- noeudDepart : Noeud - Le noeud de départ des cycles. Tests ----- >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E') >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1) >>> BA = Arc('B->A',B,A,1) >>> CB = Arc('C->B',C,B,1) >>> DE = Arc('D->E',D,E,2) >>> EA = Arc('E->A',E,A,3) >>> g = Graphe('graphe') >>> g.setNoeuds([A,B,C,D,E]) >>> g.setArcs([AB,AC,AD,CB,DE,EA]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'A->D', 'C->B', 'D->E', 'E->A']) >>> for cycle in g.getAllNodesCyclesFromNoeud(A): ... print("====cycle====") ... for n in cycle: ... print(n.getNom()) ... ====cycle==== A D E A >>> g1 = Graphe('graphe1') >>> g1.setNoeuds([A,B,C,D,E]) >>> g1.setArcs([AB,AC,BA,CB,DE,EA]) >>> print(g1) Graphe(nom=graphe1, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'B->A', 'C->B', 'D->E', 'E->A']) >>> for cycle in g1.getAllNodesCyclesFromNoeud(A): ... print("====cycle====") ... for n in cycle: ... print(n.getNom()) ... ====cycle==== A B A ====cycle==== A C B A >>> A1,B1,C1 = Noeud('A'),Noeud('B'),Noeud('C') >>> A1B1,A1B1bis,A1C1 = Arc('A-5->B',A1,B1,5),Arc('A-2->B',A1,B1,2),Arc('A-3->C',A1,C1,3) >>> B1C1,C1A1,C1A1bis = Arc('B-5->C',B1,C1,5),Arc('C-8->A',C1,A1,8),Arc('C-1->A',C1,A1,1) >>> g2 = Graphe('graphe2') >>> g2.setNoeuds([A1,B1,C1]) >>> g2.setArcs([A1B1,A1B1bis,A1C1,B1C1,C1A1,C1A1bis]) >>> print(g2) Graphe(nom=graphe2, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A']) >>> # Il y a 6 cycles distincts partant de A1 >>> # On les affiche tous, ils semblent en doublon mais ce sont les arcs les liant qui diffèrent. >>> for cycle in g2.getAllNodesCyclesFromNoeud(A1): ... print("====cycle====") ... for n in cycle: ... print(n.getNom()) ... ====cycle==== A B C A ====cycle==== A B C A ====cycle==== A B C A ====cycle==== A B C A ====cycle==== A C A ====cycle==== A C A """ # noqa : E501 pass assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud." # noqa : E501 allCycles = [] if self.hasCycleFromNoeud(noeudDepart): for noeud in self.getCreanciers(noeudDepart): for i in range(len(self.getNodesWays(noeud, noeudDepart))): cycle = [] cycle = [noeudDepart] cycle.extend(self.getNodesWays(noeud, noeudDepart)[i]) allCycles.append(cycle) return allCycles
def getArcInverse(self, arcCandidat: models.arc.Arc) ‑> models.arc.Arc
-
Renvoie l'arc inverse d'arcCandidat s'il en a un, plante sinon.
Paramètres
arcCandidat : Arc - L'arc dont on veut récupérer l'arc inverse.
Remarques
- L'arc inverse n'est pas forcément un arc inverse dans le sens que l'arc d'un cycle de Klein peut être un arc inverse introduit au debut de l'algorithme.
- On suppose que le graphe est simplifié au préalable afin qu'il n'y ait qu'un seul arc entre deux noeuds dans un sens donné.
Tests
>>> g = Graphe('graphe') >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E') >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1) >>> BA = Arc('B->A',B,A,1) >>> CB = Arc('C->B',C,B,1) >>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1) >>> EA = Arc('E->A',E,A,1) >>> g.setNoeuds([A,B,C,D,E]) >>> g.setArcs([AB,AC,AD,BA,CB,DE,DB,EA]) >>> # On ajoute tous les arcs inverses au graphe >>> g.addArcsInversesNuls() >>> print(AB.getNom()) A->B >>> print(g.getArcInverse(AB).getNom()) B-0->A
Expand source code
def getArcInverse(self, arcCandidat: Arc) -> Arc: """Renvoie l'arc inverse d'arcCandidat s'il en a un, plante sinon. Paramètres ---------- arcCandidat : Arc - L'arc dont on veut récupérer l'arc inverse. Remarques --------- - L'arc inverse n'est pas forcément un arc inverse dans le sens que l'arc d'un cycle de Klein peut être un arc inverse introduit au debut de l'algorithme. - On suppose que le graphe est simplifié au préalable afin qu'il n'y ait qu'un seul arc entre deux noeuds dans un sens donné. Tests ----- >>> g = Graphe('graphe') >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E') >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1) >>> BA = Arc('B->A',B,A,1) >>> CB = Arc('C->B',C,B,1) >>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1) >>> EA = Arc('E->A',E,A,1) >>> g.setNoeuds([A,B,C,D,E]) >>> g.setArcs([AB,AC,AD,BA,CB,DE,DB,EA]) >>> # On ajoute tous les arcs inverses au graphe >>> g.addArcsInversesNuls() >>> print(AB.getNom()) A->B >>> print(g.getArcInverse(AB).getNom()) B-0->A """ pass assert isinstance(arcCandidat, Arc), "arcCandidat doit être un instance de la classe Arc." # noqa : E501 assert self.hasArcInverse(arcCandidat), "arcCandidat doit avoir un arc inverse." # noqa : E501 arcInverse = None if self.hasArcInverse(arcCandidat): if not arcCandidat.getEstInverse(): for arc in self.getArcsInverses(): if (arc.getNoeudFin() == arcCandidat.getNoeudDebut() and arc.getNoeudDebut() == arcCandidat.getNoeudFin()): arcInverse = arc if arcCandidat.getEstInverse(): for arc in self.getArcsDirects(): if (arc.getNoeudFin() == arcCandidat.getNoeudDebut() and arc.getNoeudDebut() == arcCandidat.getNoeudFin()): arcInverse = arc return arcInverse
def getArcs(self) ‑> list
-
Renvoie le tableau des arcs du graphe.
Tests
>>> g1 = Graphe('graphe1') >>> n1 = Noeud('n1') >>> n2 = Noeud('n2') >>> n3 = Noeud('n3') >>> g1.addNoeud(n1) >>> g1.addNoeud(n2) >>> g1.addNoeud(n3) >>> a1 = Arc('n1n2_0', n1, n2, 1) >>> a2 = Arc('n1n2_1', n1, n2, 2) >>> a3 = Arc('n1n3_0', n1, n3, 3) >>> a4 = Arc('n2n3_0', n2, n3, 4) >>> g1.addArc(a1) >>> g1.addArc(a2) >>> g1.addArc(a3) >>> g1.addArc(a4) >>> for arc in g1.getArcs(): ... # On n'affiche que les noms des arcs ... print(arc.nom) ... n1n2_0 n1n2_1 n1n3_0 n2n3_0 >>> for noeud in g1.getNoeuds(): ... print(noeud) ... Noeud(nom=n1, voisins=['n2', 'n3'], nbArcs=3, capital=0) Noeud(nom=n2, voisins=['n1', 'n3'], nbArcs=3, capital=0) Noeud(nom=n3, voisins=['n1', 'n2'], nbArcs=2, capital=0)
Expand source code
def getArcs(self) -> list: """Renvoie le tableau des arcs du graphe. Tests ----- >>> g1 = Graphe('graphe1') >>> n1 = Noeud('n1') >>> n2 = Noeud('n2') >>> n3 = Noeud('n3') >>> g1.addNoeud(n1) >>> g1.addNoeud(n2) >>> g1.addNoeud(n3) >>> a1 = Arc('n1n2_0', n1, n2, 1) >>> a2 = Arc('n1n2_1', n1, n2, 2) >>> a3 = Arc('n1n3_0', n1, n3, 3) >>> a4 = Arc('n2n3_0', n2, n3, 4) >>> g1.addArc(a1) >>> g1.addArc(a2) >>> g1.addArc(a3) >>> g1.addArc(a4) >>> for arc in g1.getArcs(): ... # On n'affiche que les noms des arcs ... print(arc.nom) ... n1n2_0 n1n2_1 n1n3_0 n2n3_0 >>> for noeud in g1.getNoeuds(): ... print(noeud) ... Noeud(nom=n1, voisins=['n2', 'n3'], nbArcs=3, capital=0) Noeud(nom=n2, voisins=['n1', 'n3'], nbArcs=3, capital=0) Noeud(nom=n3, voisins=['n1', 'n2'], nbArcs=2, capital=0) """ pass return self.arcs
def getArcsBetween(self, noeudDepart: models.noeud.Noeud, noeudArrivee: models.noeud.Noeud) ‑> list
-
Renvoie tous les arcs entre noeudDepart et noeudArrivee.
Paramètres
- noeudDepart : Noeud - Le noeud de départ des arcs.
- noeudArrivee : Noeud - Le noeud de d'arrivée des arcs.
Tests
>>> g = Graphe('graphe') >>> A,B,D,E = Noeud('A'),Noeud('B'),Noeud('D'),Noeud('E') >>> AB1,AB2,AB3 = Arc('A->B(1)',A,B,1),Arc('A->B(2)',A,B,1),Arc('A->B(3)',A,B,1) >>> DE = Arc('D->E',D,E,1) >>> EA = Arc('E->A',E,A,1) >>> g.setNoeuds([A,B,D,E]) >>> g.setArcs([AB1,AB2,AB3,DE,EA]) >>> for a in g.getArcsBetween(A,B): ... print(a.getNom()) ... A->B(1) A->B(2) A->B(3) >>> # Aucun arc de D à A >>> for a in g.getArcsBetween(D,A): ... print(a.getNom()) ...
Expand source code
def getArcsBetween(self, noeudDepart: Noeud, noeudArrivee: Noeud) -> list: """Renvoie tous les arcs entre noeudDepart et noeudArrivee. Paramètres ---------- - noeudDepart : Noeud - Le noeud de départ des arcs. - noeudArrivee : Noeud - Le noeud de d'arrivée des arcs. Tests ----- >>> g = Graphe('graphe') >>> A,B,D,E = Noeud('A'),Noeud('B'),Noeud('D'),Noeud('E') >>> AB1,AB2,AB3 = Arc('A->B(1)',A,B,1),Arc('A->B(2)',A,B,1),Arc('A->B(3)',A,B,1) >>> DE = Arc('D->E',D,E,1) >>> EA = Arc('E->A',E,A,1) >>> g.setNoeuds([A,B,D,E]) >>> g.setArcs([AB1,AB2,AB3,DE,EA]) >>> for a in g.getArcsBetween(A,B): ... print(a.getNom()) ... A->B(1) A->B(2) A->B(3) >>> # Aucun arc de D à A >>> for a in g.getArcsBetween(D,A): ... print(a.getNom()) ... """ # noqa : E501 pass assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud." # noqa : E501 assert isinstance(noeudArrivee, Noeud), "Le noeudArrivee doit être un instance de la classe Noeud." # noqa : E501 arcs = [] for arc in self.getArcs(): if (arc.getNoeudDebut() == noeudDepart and arc.getNoeudFin() == noeudArrivee): arcs.append(arc) return arcs
def getArcsCreanciers(self, noeudDepart: models.noeud.Noeud) ‑> list
-
Renvoie une liste de tous les arcs partants de noeudDepart.
Paramètres
noeudDepart : Noeud - Le noeud dont on veut la liste des créanciers.
Tests
>>> g = Graphe('graphe') >>> A,B,D,E = Noeud('A'),Noeud('B'),Noeud('D'),Noeud('E') >>> AB1,AB2,AB3 = Arc('A->B(1)',A,B,1),Arc('A->B(2)',A,B,1),Arc('A->B(3)',A,B,1) >>> DE = Arc('D->E',D,E,1) >>> EA = Arc('E->A',E,A,1) >>> g.setNoeuds([A,B,D,E]) >>> g.setArcs([AB1,AB2,AB3,DE,EA]) >>> for a in g.getArcsCreanciers(A): ... print(a.getNom()) ... A->B(1) A->B(2) A->B(3) >>> for a in g.getArcsCreanciers(D): ... print(a.getNom()) ... D->E
Expand source code
def getArcsCreanciers(self, noeudDepart: Noeud) -> list: """Renvoie une liste de tous les arcs partants de noeudDepart. Paramètres ---------- noeudDepart : Noeud - Le noeud dont on veut la liste des créanciers. Tests ----- >>> g = Graphe('graphe') >>> A,B,D,E = Noeud('A'),Noeud('B'),Noeud('D'),Noeud('E') >>> AB1,AB2,AB3 = Arc('A->B(1)',A,B,1),Arc('A->B(2)',A,B,1),Arc('A->B(3)',A,B,1) >>> DE = Arc('D->E',D,E,1) >>> EA = Arc('E->A',E,A,1) >>> g.setNoeuds([A,B,D,E]) >>> g.setArcs([AB1,AB2,AB3,DE,EA]) >>> for a in g.getArcsCreanciers(A): ... print(a.getNom()) ... A->B(1) A->B(2) A->B(3) >>> for a in g.getArcsCreanciers(D): ... print(a.getNom()) ... D->E """ # noqa : E501 pass assert self.isNoeudInGraphe(noeudDepart), "noeudDepart doit être un noeud du graphe." # noqa : E501 # Ne pas utiliser les assertions pour valider les données # car elles peuvent être désactivées. # On lève donc une erreur en cas de souci if not self.isNoeudInGraphe(noeudDepart): raise ValueError("noeudDepart doit être un noeud du graphe.") else: arcsCreanciers = [] for arc in self.getArcs(): if arc.getNoeudDebut() == noeudDepart: arcsCreanciers.append(arc) return arcsCreanciers
def getArcsCycleFromNoeud(self, noeudDepart: models.noeud.Noeud) ‑> list
-
Renvoie une liste d'arcs représentant un cycle partant du noeudDepart s'il en existe. [] sinon.
Paramètres
- noeudDepart : Noeud - Le noeud de départ du cycle.
Remarque
S'il existe plusieurs cycles à partir d'un noeud, l'algorithme n'en renvoie qu'un seul au hasard.
Tests
>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3) >>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1) >>> g2 = Graphe('graphe2') >>> g2.setNoeuds([A,B,C]) >>> g2.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) >>> print(g2) Graphe(nom=graphe2, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A']) >>> # Il y a 6 cycles partants de A dans ce graphe >>> # On les récupère tous en fixant la graine >>> random.seed(1) >>> for arc in g2.getArcsCycleFromNoeud(A): ... print(arc.getNom()) ... A-5->B B-5->C C-1->A >>> random.seed(2) >>> for arc in g2.getArcsCycleFromNoeud(A): ... print(arc.getNom()) ... A-5->B B-5->C C-8->A >>> random.seed(6) >>> for arc in g2.getArcsCycleFromNoeud(A): ... print(arc.getNom()) ... A-2->B B-5->C C-1->A >>> random.seed(4) >>> for arc in g2.getArcsCycleFromNoeud(A): ... print(arc.getNom()) ... A-2->B B-5->C C-8->A >>> random.seed(5) >>> for arc in g2.getArcsCycleFromNoeud(A): ... print(arc.getNom()) ... A-3->C C-1->A >>> random.seed(24) >>> for arc in g2.getArcsCycleFromNoeud(A): ... print(arc.getNom()) ... A-3->C C-8->A
Expand source code
def getArcsCycleFromNoeud(self, noeudDepart: Noeud) -> list: """Renvoie une liste d'arcs représentant un cycle partant du noeudDepart s'il en existe. [] sinon. Paramètres ---------- - noeudDepart : Noeud - Le noeud de départ du cycle. Remarque -------- S'il existe plusieurs cycles à partir d'un noeud, l'algorithme n'en renvoie qu'un seul au hasard. Tests ----- >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3) >>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1) >>> g2 = Graphe('graphe2') >>> g2.setNoeuds([A,B,C]) >>> g2.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) >>> print(g2) Graphe(nom=graphe2, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A']) >>> # Il y a 6 cycles partants de A dans ce graphe >>> # On les récupère tous en fixant la graine >>> random.seed(1) >>> for arc in g2.getArcsCycleFromNoeud(A): ... print(arc.getNom()) ... A-5->B B-5->C C-1->A >>> random.seed(2) >>> for arc in g2.getArcsCycleFromNoeud(A): ... print(arc.getNom()) ... A-5->B B-5->C C-8->A >>> random.seed(6) >>> for arc in g2.getArcsCycleFromNoeud(A): ... print(arc.getNom()) ... A-2->B B-5->C C-1->A >>> random.seed(4) >>> for arc in g2.getArcsCycleFromNoeud(A): ... print(arc.getNom()) ... A-2->B B-5->C C-8->A >>> random.seed(5) >>> for arc in g2.getArcsCycleFromNoeud(A): ... print(arc.getNom()) ... A-3->C C-1->A >>> random.seed(24) >>> for arc in g2.getArcsCycleFromNoeud(A): ... print(arc.getNom()) ... A-3->C C-8->A """ # noqa : E501 pass assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud." # noqa : E501 arcsCycle = [] if self.hasCycleFromNoeud(noeudDepart): noeudsPossibles = [] for noeud in self.getCreanciers(noeudDepart): if (self.getNodesWays(noeud, noeudDepart) and noeud not in noeudsPossibles): noeudsPossibles.append(noeud) # On choisit aléatoirement un premier noeud parmi ceux # qui permettent de revenir à noeudDepart noeudAleatoire = random.choice(noeudsPossibles) # On ajoute le début du chemin arcsCycle.extend(self.getArcsWay(noeudDepart, noeudAleatoire)) # On ajoute le reste du chemin arcsCycle.extend(self.getArcsWay(noeudAleatoire, noeudDepart)) return arcsCycle
def getArcsDirects(self) ‑> list
-
Renvoie la liste des arcs directs du graphe.
Tests
>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3) >>> g = Graphe('graphe') >>> g.setNoeuds([A,B,C]) >>> g.setArcs([AB5,AB2,AC3]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C']) >>> # Pour les tests, on ajoute les arcs inverses à la main >>> # car la méthode addArcsInversesNuls() simplifie le graphe s'il ne l'est pas >>> BA5i,BA2i,CA3i = Arc('B-5->A(i)',B,A,0),Arc('B-2->A(i)',B,A,0),Arc('C-3->A(i)',C,A,0) >>> arcsInverses = [BA5i,BA2i,CA3i] >>> for arc in arcsInverses: ... arc.setEstInverse(True) >>> g.addArcs([BA5i,BA2i,CA3i]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->A(i)', 'B-2->A(i)', 'C-3->A(i)']) >>> for arc in g.getArcsDirects(): ... print(arc.getNom()) ... A-5->B A-2->B A-3->C >>> g.addArcsInversesNuls() >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-0->A', 'C-0->A']) >>> for arc in g.getArcsDirects(): ... print(arc.getNom()) ... A-7->B A-3->C
Expand source code
def getArcsDirects(self) -> list: """Renvoie la liste des arcs directs du graphe. Tests ----- >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3) >>> g = Graphe('graphe') >>> g.setNoeuds([A,B,C]) >>> g.setArcs([AB5,AB2,AC3]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C']) >>> # Pour les tests, on ajoute les arcs inverses à la main >>> # car la méthode addArcsInversesNuls() simplifie le graphe s'il ne l'est pas >>> BA5i,BA2i,CA3i = Arc('B-5->A(i)',B,A,0),Arc('B-2->A(i)',B,A,0),Arc('C-3->A(i)',C,A,0) >>> arcsInverses = [BA5i,BA2i,CA3i] >>> for arc in arcsInverses: ... arc.setEstInverse(True) >>> g.addArcs([BA5i,BA2i,CA3i]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->A(i)', 'B-2->A(i)', 'C-3->A(i)']) >>> for arc in g.getArcsDirects(): ... print(arc.getNom()) ... A-5->B A-2->B A-3->C >>> g.addArcsInversesNuls() >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-0->A', 'C-0->A']) >>> for arc in g.getArcsDirects(): ... print(arc.getNom()) ... A-7->B A-3->C """ # noqa : E501 pass arcsDirects = [] for arc in self.getArcs(): if not arc.getEstInverse(): arcsDirects.append(arc) return arcsDirects
def getArcsInverses(self) ‑> list
-
Renvoie la liste des arcs inverses du graphe.
Tests
>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3) >>> g = Graphe('graphe') >>> g.setNoeuds([A,B,C]) >>> g.setArcs([AB5,AB2,AC3]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C']) >>> # Pour les tests, on ajoute les arcs inverses à la main >>> # car la méthode addArcsInversesNuls() simplifie le graphe s'il ne l'est pas >>> BA5i,BA2i,CA3i = Arc('B-5->A(i)',B,A,0,True),Arc('B-2->A(i)',B,A,0,True),Arc('C-3->A(i)',C,A,0,True) >>> arcsInverses = [BA5i,BA2i,CA3i] >>> g.addArcs([BA5i,BA2i,CA3i]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->A(i)', 'B-2->A(i)', 'C-3->A(i)']) >>> for arc in g.getArcsInverses(): ... print(arc.getNom()) B-5->A(i) B-2->A(i) C-3->A(i) >>> g.addArcsInversesNuls() >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-0->A', 'C-0->A']) >>> for arc in g.getArcsInverses(): ... print(arc.getNom()) ... B-0->A C-0->A
Expand source code
def getArcsInverses(self) -> list: """Renvoie la liste des arcs inverses du graphe. Tests ----- >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3) >>> g = Graphe('graphe') >>> g.setNoeuds([A,B,C]) >>> g.setArcs([AB5,AB2,AC3]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C']) >>> # Pour les tests, on ajoute les arcs inverses à la main >>> # car la méthode addArcsInversesNuls() simplifie le graphe s'il ne l'est pas >>> BA5i,BA2i,CA3i = Arc('B-5->A(i)',B,A,0,True),Arc('B-2->A(i)',B,A,0,True),Arc('C-3->A(i)',C,A,0,True) >>> arcsInverses = [BA5i,BA2i,CA3i] >>> g.addArcs([BA5i,BA2i,CA3i]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->A(i)', 'B-2->A(i)', 'C-3->A(i)']) >>> for arc in g.getArcsInverses(): ... print(arc.getNom()) B-5->A(i) B-2->A(i) C-3->A(i) >>> g.addArcsInversesNuls() >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-0->A', 'C-0->A']) >>> for arc in g.getArcsInverses(): ... print(arc.getNom()) ... B-0->A C-0->A """ # noqa : E501 pass arcsInverses = [] for arc in self.getArcs(): if arc.getEstInverse(): arcsInverses.append(arc) return arcsInverses
def getArcsWay(self, noeudDepart: models.noeud.Noeud, noeudArrivee: models.noeud.Noeud) ‑> list
-
Renvoie une liste d'arcs contenant un chemin entre noeudDepart et noeudArrivee s'il en existe. Renvoie [] sinon.
Paramètres
- noeudDepart : Noeud - Le noeud de départ des arcs du chemin.
- noeudArrivee : Noeud - Le noeud de d'arrivée des arcs du chemin.
Remarque
Ne trouve pas les cycles Choisit un arc au hasard s'il existe plusieurs arcs parallèles.
noeudDepart : Noeud - noeud de départ noeudArrivee : Noeud - noeud d'arrivée
Tests
>>> g = Graphe('graphe') >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E') >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1) >>> CB = Arc('C->B',C,B,1) >>> CBbis = Arc('C-2->B',C,B,2) >>> DE = Arc('D->E',D,E,1) >>> EA = Arc('E->A',E,A,1) >>> g.setNoeuds([A,B,C,D,E]) >>> g.setArcs([AB,AC,AD,CB,CBbis,DE,EA]) >>> # Aucun chemin de B à C >>> for arc in g.getArcsWay(B,C): ... print(arc) ... >>> # Dans ce graphe trois chemins sont possibles entre A et B >>> # Deux des trois chemins présentent des arcs parallèles. >>> # On fixe la graine pour les tests >>> random.seed(13) >>> for arc in g.getArcsWay(A,B): ... print(arc.getNom()) ... A->C C->B >>> random.seed(27) >>> for arc in g.getArcsWay(A,B): ... print(arc.getNom()) ... A->C C-2->B >>> random.seed(3) >>> for arc in g.getArcsWay(A,B): ... print(arc.getNom()) ... A->B
Expand source code
def getArcsWay(self, noeudDepart: Noeud, noeudArrivee: Noeud) -> list: """Renvoie une liste d'arcs contenant un chemin entre noeudDepart et noeudArrivee s'il en existe. Renvoie [] sinon. Paramètres ---------- - noeudDepart : Noeud - Le noeud de départ des arcs du chemin. - noeudArrivee : Noeud - Le noeud de d'arrivée des arcs du chemin. Remarque -------- Ne trouve pas les cycles Choisit un arc au hasard s'il existe plusieurs arcs parallèles. noeudDepart : Noeud - noeud de départ noeudArrivee : Noeud - noeud d'arrivée Tests ----- >>> g = Graphe('graphe') >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E') >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1) >>> CB = Arc('C->B',C,B,1) >>> CBbis = Arc('C-2->B',C,B,2) >>> DE = Arc('D->E',D,E,1) >>> EA = Arc('E->A',E,A,1) >>> g.setNoeuds([A,B,C,D,E]) >>> g.setArcs([AB,AC,AD,CB,CBbis,DE,EA]) >>> # Aucun chemin de B à C >>> for arc in g.getArcsWay(B,C): ... print(arc) ... >>> # Dans ce graphe trois chemins sont possibles entre A et B >>> # Deux des trois chemins présentent des arcs parallèles. >>> # On fixe la graine pour les tests >>> random.seed(13) >>> for arc in g.getArcsWay(A,B): ... print(arc.getNom()) ... A->C C->B >>> random.seed(27) >>> for arc in g.getArcsWay(A,B): ... print(arc.getNom()) ... A->C C-2->B >>> random.seed(3) >>> for arc in g.getArcsWay(A,B): ... print(arc.getNom()) ... A->B """ # noqa : E501 pass assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud." # noqa : E501 assert isinstance(noeudArrivee, Noeud), "Le noeudArrivee doit être un instance de la classe Noeud." # noqa : E501 if self.getNodesWays(noeudDepart, noeudArrivee): arcsWay = random.choice( self.getArcsWays(noeudDepart, noeudArrivee) ) else: arcsWay = [] return arcsWay
def getArcsWays(self, noeudDepart: models.noeud.Noeud, noeudArrivee: models.noeud.Noeud, way=None) ‑> list
-
Renvoie une liste de tuples d'arcs contenant tous les chemins entre noeudDepart et noeudArrivee s'il en existe. Renvoie [] sinon.
Paramètres
- noeudDepart : Noeud - Le noeud de départ du chemin.
- noeudArrivee : Noeud - Le noeud d'arrivée du chemin.
- way : list - un tableau contenant les arcs permettant d'aller de noeudDepart à noeudArrivee.
Remarques
- Ne trouve pas les cycles
- S'il existe plusieurs arcs entre deux noeuds, le chemin de noeud est identique et ressort donc plusieurs fois. Ce sera utile pour compter les chemins d'arcs distincts.
Tests
>>> g = Graphe('grapheLinéaire') >>> A,B,C,D = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D') >>> AB1,AB2,AB3 = Arc('A->B(1)',A,B,1),Arc('A->B(2)',A,B,1),Arc('A->B(3)',A,B,1) >>> BC1,BC2 = Arc('B->C(1)',B,C,1),Arc('B->C(2)',B,C,1) >>> CD1,CD2,CD3,CD4 = Arc('C->D(1)',C,D,1),Arc('C->D(2)',C,D,1),Arc('C->D(3)',C,D,1),Arc('C->D(4)',C,D,1) >>> g.setNoeuds([A,B,C,D]) >>> g.setArcs([AB1,AB2,AB3,BC1,BC2,CD1,CD2,CD3,CD4]) >>> # Il y a 24 chemins d'arcs possibles entre A et D >>> len(g.getArcsWays(A,D)) 24 >>> # Il y a 6 chemins d'arcs possibles entre A et C >>> # On les liste tous >>> len(g.getArcsWays(A,C)) 6 >>> for arcsWay in g.getArcsWays(A,C): ... print("=== Chemin d'arcs entre A et C ===") ... for arc in arcsWay: ... print(arc.getNom()) ... === Chemin d'arcs entre A et C === A->B(1) B->C(1) === Chemin d'arcs entre A et C === A->B(1) B->C(2) === Chemin d'arcs entre A et C === A->B(2) B->C(1) === Chemin d'arcs entre A et C === A->B(2) B->C(2) === Chemin d'arcs entre A et C === A->B(3) B->C(1) === Chemin d'arcs entre A et C === A->B(3) B->C(2) >>> # Tests sur un graphe non linéaire >>> # On ajoute deux arcs entre C et A >>> CA1,CA2 = Arc('C->A(1)',C,A,1),Arc('C->A(2)',C,A,1) >>> # On ajoute un arc entre A et C >>> AC1 = Arc('A->C(1)',A,C,1) >>> # Le graphe aura donc un cycle ABCA >>> # La méthode ne tolèrant pas de repasser par le noeud de départ >>> # Elle trouve bien 7 chemins entre A et C >>> g1 = Graphe('grapheNonLinéaire') >>> g1.setNoeuds([A,B,C,D]) >>> g1.setArcs([AB1,AB2,AB3,BC1,BC2,CD1,CD2,CD3,CD4,CA1,CA2,AC1]) >>> len(g1.getArcsWays(A,C)) 7 >>> for arcsWay in g1.getArcsWays(A,C): ... print("=== Chemin d'arcs entre A et C ===") ... for arc in arcsWay: ... print(arc.getNom()) ... === Chemin d'arcs entre A et C === A->B(1) B->C(1) === Chemin d'arcs entre A et C === A->B(1) B->C(2) === Chemin d'arcs entre A et C === A->B(2) B->C(1) === Chemin d'arcs entre A et C === A->B(2) B->C(2) === Chemin d'arcs entre A et C === A->B(3) B->C(1) === Chemin d'arcs entre A et C === A->B(3) B->C(2) === Chemin d'arcs entre A et C === A->C(1)
Expand source code
def getArcsWays( self, noeudDepart: Noeud, noeudArrivee: Noeud, way=None ) -> list: """Renvoie une liste de tuples d'arcs contenant tous les chemins entre noeudDepart et noeudArrivee s'il en existe. Renvoie [] sinon. Paramètres ---------- - noeudDepart : Noeud - Le noeud de départ du chemin. - noeudArrivee : Noeud - Le noeud d'arrivée du chemin. - way : list - un tableau contenant les arcs permettant d'aller de noeudDepart à noeudArrivee. Remarques --------- - Ne trouve pas les cycles - S'il existe plusieurs arcs entre deux noeuds, le chemin de noeud est identique et ressort donc plusieurs fois. Ce sera utile pour compter les chemins d'arcs distincts. Tests ----- >>> g = Graphe('grapheLinéaire') >>> A,B,C,D = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D') >>> AB1,AB2,AB3 = Arc('A->B(1)',A,B,1),Arc('A->B(2)',A,B,1),Arc('A->B(3)',A,B,1) >>> BC1,BC2 = Arc('B->C(1)',B,C,1),Arc('B->C(2)',B,C,1) >>> CD1,CD2,CD3,CD4 = Arc('C->D(1)',C,D,1),Arc('C->D(2)',C,D,1),Arc('C->D(3)',C,D,1),Arc('C->D(4)',C,D,1) >>> g.setNoeuds([A,B,C,D]) >>> g.setArcs([AB1,AB2,AB3,BC1,BC2,CD1,CD2,CD3,CD4]) >>> # Il y a 24 chemins d'arcs possibles entre A et D >>> len(g.getArcsWays(A,D)) 24 >>> # Il y a 6 chemins d'arcs possibles entre A et C >>> # On les liste tous >>> len(g.getArcsWays(A,C)) 6 >>> for arcsWay in g.getArcsWays(A,C): ... print("=== Chemin d'arcs entre A et C ===") ... for arc in arcsWay: ... print(arc.getNom()) ... === Chemin d'arcs entre A et C === A->B(1) B->C(1) === Chemin d'arcs entre A et C === A->B(1) B->C(2) === Chemin d'arcs entre A et C === A->B(2) B->C(1) === Chemin d'arcs entre A et C === A->B(2) B->C(2) === Chemin d'arcs entre A et C === A->B(3) B->C(1) === Chemin d'arcs entre A et C === A->B(3) B->C(2) >>> # Tests sur un graphe non linéaire >>> # On ajoute deux arcs entre C et A >>> CA1,CA2 = Arc('C->A(1)',C,A,1),Arc('C->A(2)',C,A,1) >>> # On ajoute un arc entre A et C >>> AC1 = Arc('A->C(1)',A,C,1) >>> # Le graphe aura donc un cycle ABCA >>> # La méthode ne tolèrant pas de repasser par le noeud de départ >>> # Elle trouve bien 7 chemins entre A et C >>> g1 = Graphe('grapheNonLinéaire') >>> g1.setNoeuds([A,B,C,D]) >>> g1.setArcs([AB1,AB2,AB3,BC1,BC2,CD1,CD2,CD3,CD4,CA1,CA2,AC1]) >>> len(g1.getArcsWays(A,C)) 7 >>> for arcsWay in g1.getArcsWays(A,C): ... print("=== Chemin d'arcs entre A et C ===") ... for arc in arcsWay: ... print(arc.getNom()) ... === Chemin d'arcs entre A et C === A->B(1) B->C(1) === Chemin d'arcs entre A et C === A->B(1) B->C(2) === Chemin d'arcs entre A et C === A->B(2) B->C(1) === Chemin d'arcs entre A et C === A->B(2) B->C(2) === Chemin d'arcs entre A et C === A->B(3) B->C(1) === Chemin d'arcs entre A et C === A->B(3) B->C(2) === Chemin d'arcs entre A et C === A->C(1) """ # noqa : E501 pass assert isinstance(noeudDepart, Noeud), "noeudDepart doit être un instance de la classe Noeud." # noqa : E501 assert isinstance(noeudArrivee, Noeud), "Le noeudArrivee doit être un instance de la classe Noeud." # noqa : E501 assert isinstance(way, list) or way is None, "way vaut None ou est une liste." # noqa : E501 assert noeudDepart != noeudArrivee, "noeudDepart et nouedArrivee doivent être distincts" # noqa : E501 # Pour stocker tous les chemins d'arcs possibles arcsWays = [] # On liste tous les chemins de noeuds cheminsDeNoeudsPossibles = self.getNodesWays(noeudDepart, noeudArrivee) cheminsDeNoeudsSansDoublons = [] for chemin in cheminsDeNoeudsPossibles: if chemin not in cheminsDeNoeudsSansDoublons: cheminsDeNoeudsSansDoublons.append(chemin) # Pour chaque chemin de cheminsDeNoeudsSansDoublon # On va récupérer tous les arcs entre deux noeuds successifs # dans arcsWay for chemin in cheminsDeNoeudsSansDoublons: # le nombre d'espace inter Noeuds nbInterNoeuds = len(chemin)-1 # Pour stocker tous les arcs possibles entre deux noeuds du chemin arcsWay = [] for i in range(nbInterNoeuds): arcsWay.append(self.getArcsBetween(chemin[i], chemin[i + 1])) # On va stocker toutes les combinaisons d'arcs de ce chemin produitCartesienDesArcs = [] for combs in product(*arcsWay): produitCartesienDesArcs.append(combs) arcsWays.extend(produitCartesienDesArcs) return arcsWays
def getCreanciers(self, noeudCandidat: models.noeud.Noeud) ‑> list
-
Récupére dans une liste, tous les noeuds créanciers du noeudCandidat.
Paramètres
noeudCandidat : Noeud - Une instance de la classe Noeud dont on veut les créanciers.
Tests
>>> g1 = Graphe('graphe1') >>> n1,n2,n3,n4,n5 = Noeud('n1'),Noeud('n2'),Noeud('n3'),Noeud('n4'),Noeud('n5') >>> g1.setNoeuds([n1,n2,n3,n4,n5]) >>> a1,a2,a3,a4 = Arc('n2n1',n2,n1,1),Arc('n3n1',n3,n1,2),Arc('n4n1',n4,n1,3),Arc('n1n4',n1,n4,4) >>> a5,a6 = Arc('n1n3',n1,n3,5),Arc('n1n5',n1,n5,6) >>> g1.setArcs([a1,a2,a3,a4,a5,a6]) >>> # On affiche uniquement les noms des noeuds créanciers pour le doctest >>> for noeud in g1.getCreanciers(n1): ... print(noeud.nom) ... n4 n3 n5 >>> for noeud in g1.getCreanciers(n2): ... print(noeud.nom) ... n1 >>> for noeud in g1.getCreanciers(n5): ... print(noeud.nom) ...
Expand source code
def getCreanciers(self, noeudCandidat: Noeud) -> list: """Récupére dans une liste, tous les noeuds créanciers du noeudCandidat. Paramètres ---------- noeudCandidat : Noeud - Une instance de la classe Noeud dont on veut les créanciers. Tests ----- >>> g1 = Graphe('graphe1') >>> n1,n2,n3,n4,n5 = Noeud('n1'),Noeud('n2'),Noeud('n3'),Noeud('n4'),Noeud('n5') >>> g1.setNoeuds([n1,n2,n3,n4,n5]) >>> a1,a2,a3,a4 = Arc('n2n1',n2,n1,1),Arc('n3n1',n3,n1,2),Arc('n4n1',n4,n1,3),Arc('n1n4',n1,n4,4) >>> a5,a6 = Arc('n1n3',n1,n3,5),Arc('n1n5',n1,n5,6) >>> g1.setArcs([a1,a2,a3,a4,a5,a6]) >>> # On affiche uniquement les noms des noeuds créanciers pour le doctest >>> for noeud in g1.getCreanciers(n1): ... print(noeud.nom) ... n4 n3 n5 >>> for noeud in g1.getCreanciers(n2): ... print(noeud.nom) ... n1 >>> for noeud in g1.getCreanciers(n5): ... print(noeud.nom) ... """ # noqa : E501 pass assert self.isNoeudInGraphe(noeudCandidat), "noeudCandidat doit être un noeud du graphe." # noqa : E501 # Ne pas utiliser les assertions pour valider les données # car elles peuvent être désactivées. # On lève donc une erreur en cas de souci if not self.isNoeudInGraphe(noeudCandidat): raise ValueError("noeudCandidat doit être un noeud du graphe.") else: noeudsCreanciers = [] for arc in self.getArcs(): if arc.getNoeudDebut() == noeudCandidat: noeudsCreanciers.append(arc.getNoeudFin()) return noeudsCreanciers
def getDebiteurs(self, noeudCandidat: models.noeud.Noeud) ‑> list
-
Récupére dans une liste, tous les noeuds débiteurs du noeudCandidat.
Paramètres
noeudCandidat : Noeud - Une instance de la classe Noeud dont on veut les débiteurs.
Tests
>>> g1 = Graphe('graphe1') >>> n1,n2,n3,n4 = Noeud('n1'),Noeud('n2'),Noeud('n3'),Noeud('n4') >>> g1.setNoeuds([n1,n2,n3,n4]) >>> a1,a2,a3,a4 = Arc('n2n1',n2,n1,1),Arc('n3n1',n3,n1,2),Arc('n4n1',n4,n1,3),Arc('n1n4',n1,n4,4) >>> g1.setArcs([a1,a2,a3,a4]) >>> # On affiche uniquement les noms des noeuds débiteurs pour le doctest >>> for noeud in g1.getDebiteurs(n1): ... print(noeud.nom) ... n2 n3 n4 >>> for noeud in g1.getDebiteurs(n2): ... print(noeud.nom) ...
>>> for noeud in g1.getDebiteurs(n4): ... print(noeud.nom) ... n1
Expand source code
def getDebiteurs(self, noeudCandidat: Noeud) -> list: """Récupére dans une liste, tous les noeuds débiteurs du noeudCandidat. Paramètres ---------- noeudCandidat : Noeud - Une instance de la classe Noeud dont on veut les débiteurs. Tests ----- >>> g1 = Graphe('graphe1') >>> n1,n2,n3,n4 = Noeud('n1'),Noeud('n2'),Noeud('n3'),Noeud('n4') >>> g1.setNoeuds([n1,n2,n3,n4]) >>> a1,a2,a3,a4 = Arc('n2n1',n2,n1,1),Arc('n3n1',n3,n1,2),Arc('n4n1',n4,n1,3),Arc('n1n4',n1,n4,4) >>> g1.setArcs([a1,a2,a3,a4]) >>> # On affiche uniquement les noms des noeuds débiteurs pour le doctest >>> for noeud in g1.getDebiteurs(n1): ... print(noeud.nom) ... n2 n3 n4 >>> for noeud in g1.getDebiteurs(n2): ... print(noeud.nom) ... >>> for noeud in g1.getDebiteurs(n4): ... print(noeud.nom) ... n1 """ # noqa : E501 pass assert self.isNoeudInGraphe(noeudCandidat), "noeudCandidat doit être un noeud du graphe." # noqa : E501 # Ne pas utiliser les assertions pour valider les données # car elles peuvent être désactivées. # On lève donc une erreur en cas de souci if not self.isNoeudInGraphe(noeudCandidat): raise ValueError("noeudCandidat doit être un noeud du graphe.") else: noeudsDebiteurs = [] for arc in self.getArcs(): if arc.getNoeudFin() == noeudCandidat: noeudsDebiteurs.append(arc.getNoeudDebut()) return noeudsDebiteurs
def getEntreprises(self) ‑> list
-
Récupère dans une liste tous les noeuds du graphe, qui sont les entrepises de la situation problème étudiée.
Tests
>>> g1 = Graphe('graphe1') >>> g1.getEntreprises() [] >>> n1,n2,n3 = Noeud('n1'),Noeud('n2'),Noeud('n3') >>> g1.setNoeuds([n1,n2,n3]) >>> # On affiche uniquement les noms des noeuds pour le doctest >>> for noeud in g1.getEntreprises(): ... print(noeud.nom) ... n1 n2 n3
Expand source code
def getEntreprises(self) -> list: """Récupère dans une liste tous les noeuds du graphe, qui sont les entrepises de la situation problème étudiée. Tests ----- >>> g1 = Graphe('graphe1') >>> g1.getEntreprises() [] >>> n1,n2,n3 = Noeud('n1'),Noeud('n2'),Noeud('n3') >>> g1.setNoeuds([n1,n2,n3]) >>> # On affiche uniquement les noms des noeuds pour le doctest >>> for noeud in g1.getEntreprises(): ... print(noeud.nom) ... n1 n2 n3 """ # noqa : E501 pass return self.getNoeuds()
def getMinPoidsFromCycle(self, cycle: list) ‑> int
-
Renvoie le plus petits poids des arcs contenus dans cycle.
Paramètres
cycle : list - Une liste d'arcs du graphe.
Remarque
Ici on gère les éventuels cycles parallèles
Tests
>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3) >>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1) >>> g = Graphe('graphe') >>> g.setNoeuds([A,B,C]) >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A']) >>> random.seed(None) >>> random.seed(9) >>> cycle = g.getArcsCycleFromNoeud(A) >>> for e in cycle: ... print(e.getNom()) ... A-3->C C-1->A >>> g.getMinPoidsFromCycle(cycle) 1 >>> # Sans simplifier le graphe il peut y avoir des cycles parallèles en partant d'un noeud >>> random.seed(15) >>> cycle = g.getArcsCycleFromNoeud(A) >>> for e in cycle: ... print(e.getNom()) ... A-5->B B-5->C C-8->A >>> g.getMinPoidsFromCycle(cycle) 5 >>> random.seed(13) >>> cycle = g.getArcsCycleFromNoeud(A) >>> for e in cycle: ... print(e.getNom()) ... A-2->B B-5->C C-8->A >>> g.getMinPoidsFromCycle(cycle) 2 >>> random.seed(17) >>> cycle = g.getArcsCycleFromNoeud(A) >>> for e in cycle: ... print(e.getNom()) ... A-2->B B-5->C C-1->A >>> g.getMinPoidsFromCycle(cycle) 1 >>> # Un test avec simplification préalable, il n'y a donc plus de cycles parallèles en partant d'un noeud. >>> # Mais il peut rester plusieurs cycles partant de A ! >>> g1 = Graphe('graphe1') >>> g1.setNoeuds([A,B,C]) >>> g1.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) >>> print(g1) Graphe(nom=graphe1, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A']) >>> g1.simplification() >>> print(g1) Graphe(nom=graphe1, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A']) >>> cycle = g1.getArcsCycleFromNoeud(A) >>> for e in cycle: ... print(e.getNom()) ... A-7->B B-5->C C-9->A >>> g1.getMinPoidsFromCycle(cycle) 5
Expand source code
def getMinPoidsFromCycle(self, cycle: list) -> int: """Renvoie le plus petits poids des arcs contenus dans cycle. Paramètres ---------- cycle : list - Une liste d'arcs du graphe. Remarque -------- Ici on gère les éventuels cycles parallèles Tests ----- >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3) >>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1) >>> g = Graphe('graphe') >>> g.setNoeuds([A,B,C]) >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A']) >>> random.seed(None) >>> random.seed(9) >>> cycle = g.getArcsCycleFromNoeud(A) >>> for e in cycle: ... print(e.getNom()) ... A-3->C C-1->A >>> g.getMinPoidsFromCycle(cycle) 1 >>> # Sans simplifier le graphe il peut y avoir des cycles parallèles en partant d'un noeud >>> random.seed(15) >>> cycle = g.getArcsCycleFromNoeud(A) >>> for e in cycle: ... print(e.getNom()) ... A-5->B B-5->C C-8->A >>> g.getMinPoidsFromCycle(cycle) 5 >>> random.seed(13) >>> cycle = g.getArcsCycleFromNoeud(A) >>> for e in cycle: ... print(e.getNom()) ... A-2->B B-5->C C-8->A >>> g.getMinPoidsFromCycle(cycle) 2 >>> random.seed(17) >>> cycle = g.getArcsCycleFromNoeud(A) >>> for e in cycle: ... print(e.getNom()) ... A-2->B B-5->C C-1->A >>> g.getMinPoidsFromCycle(cycle) 1 >>> # Un test avec simplification préalable, il n'y a donc plus de cycles parallèles en partant d'un noeud. >>> # Mais il peut rester plusieurs cycles partant de A ! >>> g1 = Graphe('graphe1') >>> g1.setNoeuds([A,B,C]) >>> g1.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) >>> print(g1) Graphe(nom=graphe1, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A']) >>> g1.simplification() >>> print(g1) Graphe(nom=graphe1, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A']) >>> cycle = g1.getArcsCycleFromNoeud(A) >>> for e in cycle: ... print(e.getNom()) ... A-7->B B-5->C C-9->A >>> g1.getMinPoidsFromCycle(cycle) 5 """ # noqa : E501 pass # les éléments de cycle doivent être des instances de la classe Arc # Ne pas utiliser les assertions pour valider les données # car elles peuvent être désactivées. # On lève donc une erreur en cas de souci # Un booléen pour tester si tout est OK allOk = True for arc in cycle: assert (isinstance(arc, Arc) for arc in cycle), "Les éléments de cycle doivent être des instances de la classe Arc" # noqa : E501 if not isinstance(arc, Arc): allOk = False raise ValueError(f'Les éléments de cycle doivent être des instances de la classe Arc. Vérifier : {arc.getNom()}') # noqa : E501 assert len(cycle) >= 1, "Le cycle doit avoir au moins un élément." if len(cycle) < 1: allOk = False raise ValueError('Le cycle doit avoir au moins un élément.') if allOk: poidsMin = cycle[0].getPoids() i = 1 while i < len(cycle): if cycle[i].getPoids() <= poidsMin: poidsMin = cycle[i].getPoids() i += 1 return poidsMin
def getNodesCycleFromNoeud(self, noeudDepart: models.noeud.Noeud) ‑> list
-
Renvoie une liste de noeuds représentant un cycle partant du noeudDepart s'il en existe. [] sinon.
Paramètres
noeudDepart : Noeud - Le noeud de départ du cycle.
Remarque
S'il existe plusieurs cycles à partir d'un noeud, l'algorithme n'en renvoie qu'un seul choisi au hasard.
Tests
>>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E') >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1) >>> BA = Arc('B->A',B,A,1) >>> CB = Arc('C->B',C,B,1) >>> DE = Arc('D->E',D,E,2) >>> EA = Arc('E->A',E,A,3) >>> g = Graphe('graphe') >>> g.setNoeuds([A,B,C,D,E]) >>> g.setArcs([AB,AC,AD,CB,DE,EA]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'A->D', 'C->B', 'D->E', 'E->A']) >>> for e in g.getNodesCycleFromNoeud(A): ... print(e.getNom()) ... A D E A >>> g.getNodesCycleFromNoeud(B) [] >>> g1 = Graphe('graphe1') >>> g1.setNoeuds([A,B,C,D,E]) >>> g1.setArcs([AB,AC,BA,CB,DE,EA]) >>> print(g1) Graphe(nom=graphe1, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'B->A', 'C->B', 'D->E', 'E->A']) >>> # Il y a deux cycles partants de A dans ce graphe >>> random.seed(3) >>> for e in g1.getNodesCycleFromNoeud(A): ... print(e.getNom()) ... A B A >>> random.seed(7) >>> for e in g1.getNodesCycleFromNoeud(A): ... print(e.getNom()) ... A C B A
Expand source code
def getNodesCycleFromNoeud(self, noeudDepart: Noeud) -> list: """Renvoie une liste de noeuds représentant un cycle partant du noeudDepart s'il en existe. [] sinon. Paramètres ---------- noeudDepart : Noeud - Le noeud de départ du cycle. Remarque -------- S'il existe plusieurs cycles à partir d'un noeud, l'algorithme n'en renvoie qu'un seul choisi au hasard. Tests ----- >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E') >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1) >>> BA = Arc('B->A',B,A,1) >>> CB = Arc('C->B',C,B,1) >>> DE = Arc('D->E',D,E,2) >>> EA = Arc('E->A',E,A,3) >>> g = Graphe('graphe') >>> g.setNoeuds([A,B,C,D,E]) >>> g.setArcs([AB,AC,AD,CB,DE,EA]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'A->D', 'C->B', 'D->E', 'E->A']) >>> for e in g.getNodesCycleFromNoeud(A): ... print(e.getNom()) ... A D E A >>> g.getNodesCycleFromNoeud(B) [] >>> g1 = Graphe('graphe1') >>> g1.setNoeuds([A,B,C,D,E]) >>> g1.setArcs([AB,AC,BA,CB,DE,EA]) >>> print(g1) Graphe(nom=graphe1, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'B->A', 'C->B', 'D->E', 'E->A']) >>> # Il y a deux cycles partants de A dans ce graphe >>> random.seed(3) >>> for e in g1.getNodesCycleFromNoeud(A): ... print(e.getNom()) ... A B A >>> random.seed(7) >>> for e in g1.getNodesCycleFromNoeud(A): ... print(e.getNom()) ... A C B A """ # noqa : E501 pass assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud." # noqa : E501 cycle = [] if self.hasCycleFromNoeud(noeudDepart): cycle.append(noeudDepart) noeudsPossibles = [] for noeud in self.getCreanciers(noeudDepart): if self.getNodesWays(noeud, noeudDepart): noeudsPossibles.append(noeud) noeudAleatoire = random.choice(noeudsPossibles) cheminsPossibles = self.getNodesWays(noeudAleatoire, noeudDepart) cheminAleatoire = random.choice(cheminsPossibles) cycle.extend(cheminAleatoire) return cycle
def getNodesWays(self, noeudDepart: models.noeud.Noeud, noeudArrivee: models.noeud.Noeud, way=None) ‑> list
-
Renvoie une liste de liste de noeuds contenant tous les chemins entre noeudDepart et noeudArrivee s'il en existe. Renvoie [] sinon.
Paramètres
- noeudDepart : Noeud - Le noeud de départ du chemin.
- noeudArrivee : Noeud - Le noeud d'arrivée du chemin.
- way : list - Un tableau contenant les noeuds permettant d'aller de noeudDepart à noeudArrivee
Remarques
- Fonction recursive
- Ne trouve pas les cycles
- S'il existe plusieurs arcs entre deux noeuds, le chemin de noeud est identique et ressort donc plusieurs fois. Ce sera utile pour compter les chemins d'arcs distincts.
Tests
>>> g = Graphe('graphe') >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E') >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1) >>> CB = Arc('C->B',C,B,1) >>> DE = Arc('D->E',D,E,1) >>> EA = Arc('E->A',E,A,1) >>> g.setNoeuds([A,B,C,D,E]) >>> g.setArcs([AB,AC,AD,CB,DE,EA]) >>> # Aucun chemin de B à C >>> for chemin in g.getNodesWays(B,C): ... print("====chemin====") ... for n in chemin: ... print(n) ... >>> # Deux chemins entre A et B >>> for chemin in g.getNodesWays(A,B): ... print("====chemin====") ... for n in chemin: ... print(n) ... ====chemin==== Noeud(nom=A, voisins=['B', 'C', 'D', 'E'], nbArcs=4, capital=0) Noeud(nom=B, voisins=['A', 'C'], nbArcs=2, capital=0) ====chemin==== Noeud(nom=A, voisins=['B', 'C', 'D', 'E'], nbArcs=4, capital=0) Noeud(nom=C, voisins=['A', 'B'], nbArcs=2, capital=0) Noeud(nom=B, voisins=['A', 'C'], nbArcs=2, capital=0) >>> g1 = Graphe('graphe1') >>> A1,B1,C1,D1 = Noeud('A1'),Noeud('B1'),Noeud('C1'),Noeud('D1') >>> A1B1_1,A1B1_2,A1B1_3 = Arc('A1->B1(1)',A1,B1,1),Arc('A1->B1(2)',A1,B1,1),Arc('A1->B1(3)',A1,B1,1) >>> B1C1_1,B1C1_2 = Arc('B1->C1(1)',B1,C1,1),Arc('B1->C1(2)',B1,C1,1) >>> C1A1 = Arc('C1->A1',C1,A1,1) >>> C1D1 = Arc('C1->D1',C1,D1,1) >>> g1.setNoeuds([A1,B1,C1,D1]) >>> g1.setArcs([A1B1_1,A1B1_2,A1B1_3,B1C1_1,B1C1_2,C1A1,C1D1]) >>> # Six chemins entre A1 et D1 >>> for chemin in g1.getNodesWays(A1,D1): ... print("====chemin====") ... for n in chemin: ... print(n) ... ====chemin==== Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0) Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0) Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0) Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0) ====chemin==== Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0) Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0) Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0) Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0) ====chemin==== Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0) Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0) Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0) Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0) ====chemin==== Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0) Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0) Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0) Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0) ====chemin==== Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0) Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0) Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0) Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0) ====chemin==== Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0) Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0) Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0) Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0)
Expand source code
def getNodesWays( self, noeudDepart: Noeud, noeudArrivee: Noeud, way=None ) -> list: """Renvoie une liste de liste de noeuds contenant tous les chemins entre noeudDepart et noeudArrivee s'il en existe. Renvoie [] sinon. Paramètres ---------- - noeudDepart : Noeud - Le noeud de départ du chemin. - noeudArrivee : Noeud - Le noeud d'arrivée du chemin. - way : list - Un tableau contenant les noeuds permettant d'aller de noeudDepart à noeudArrivee Remarques --------- - Fonction recursive - Ne trouve pas les cycles - S'il existe plusieurs arcs entre deux noeuds, le chemin de noeud est identique et ressort donc plusieurs fois. Ce sera utile pour compter les chemins d'arcs distincts. Tests ----- >>> g = Graphe('graphe') >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E') >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1) >>> CB = Arc('C->B',C,B,1) >>> DE = Arc('D->E',D,E,1) >>> EA = Arc('E->A',E,A,1) >>> g.setNoeuds([A,B,C,D,E]) >>> g.setArcs([AB,AC,AD,CB,DE,EA]) >>> # Aucun chemin de B à C >>> for chemin in g.getNodesWays(B,C): ... print("====chemin====") ... for n in chemin: ... print(n) ... >>> # Deux chemins entre A et B >>> for chemin in g.getNodesWays(A,B): ... print("====chemin====") ... for n in chemin: ... print(n) ... ====chemin==== Noeud(nom=A, voisins=['B', 'C', 'D', 'E'], nbArcs=4, capital=0) Noeud(nom=B, voisins=['A', 'C'], nbArcs=2, capital=0) ====chemin==== Noeud(nom=A, voisins=['B', 'C', 'D', 'E'], nbArcs=4, capital=0) Noeud(nom=C, voisins=['A', 'B'], nbArcs=2, capital=0) Noeud(nom=B, voisins=['A', 'C'], nbArcs=2, capital=0) >>> g1 = Graphe('graphe1') >>> A1,B1,C1,D1 = Noeud('A1'),Noeud('B1'),Noeud('C1'),Noeud('D1') >>> A1B1_1,A1B1_2,A1B1_3 = Arc('A1->B1(1)',A1,B1,1),Arc('A1->B1(2)',A1,B1,1),Arc('A1->B1(3)',A1,B1,1) >>> B1C1_1,B1C1_2 = Arc('B1->C1(1)',B1,C1,1),Arc('B1->C1(2)',B1,C1,1) >>> C1A1 = Arc('C1->A1',C1,A1,1) >>> C1D1 = Arc('C1->D1',C1,D1,1) >>> g1.setNoeuds([A1,B1,C1,D1]) >>> g1.setArcs([A1B1_1,A1B1_2,A1B1_3,B1C1_1,B1C1_2,C1A1,C1D1]) >>> # Six chemins entre A1 et D1 >>> for chemin in g1.getNodesWays(A1,D1): ... print("====chemin====") ... for n in chemin: ... print(n) ... ====chemin==== Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0) Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0) Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0) Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0) ====chemin==== Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0) Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0) Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0) Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0) ====chemin==== Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0) Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0) Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0) Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0) ====chemin==== Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0) Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0) Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0) Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0) ====chemin==== Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0) Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0) Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0) Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0) ====chemin==== Noeud(nom=A1, voisins=['B1', 'C1'], nbArcs=4, capital=0) Noeud(nom=B1, voisins=['A1', 'C1'], nbArcs=5, capital=0) Noeud(nom=C1, voisins=['B1', 'A1', 'D1'], nbArcs=4, capital=0) Noeud(nom=D1, voisins=['C1'], nbArcs=1, capital=0) """ # noqa : E501 pass assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud." # noqa : E501 assert isinstance(noeudArrivee, Noeud), "Le noeudArrivee doit être un instance de la classe Noeud." # noqa : E501 assert isinstance(way, list) or way is None, "way vaut None ou est une liste." # noqa : E501 if way is None: way = [] way = way + [noeudDepart] if noeudDepart == noeudArrivee: return [way] ways = [] nonVisites = [] # Pour l'algorithme de Klein, notamment, # le fait de rechercher tous les chemins # fait exploser le temps de traitement sur des graphes de # taille réelle dès Gen_100 creanciersNoeudDepart = self.getCreanciers(noeudDepart) for noeud in creanciersNoeudDepart: if noeud not in way: nonVisites.append(noeud) for noeud in nonVisites: ways.extend(self.getNodesWays(noeud, noeudArrivee, way)) return ways
def getNoeuds(self) ‑> list
-
Renvoie le tableau des noeuds du graphe.
Tests
>>> g1 = Graphe('graphe1') >>> n1 = Noeud('n1') >>> n2 = Noeud('n2') >>> n3 = Noeud('n3') >>> g1.addNoeud(n1) >>> g1.addNoeud(n2) >>> g1.addNoeud(n3) >>> for noeud in g1.getNoeuds(): ... print(noeud) ... Noeud(nom=n1, voisins=[], nbArcs=0, capital=0) Noeud(nom=n2, voisins=[], nbArcs=0, capital=0) Noeud(nom=n3, voisins=[], nbArcs=0, capital=0)
Expand source code
def getNoeuds(self) -> list: """Renvoie le tableau des noeuds du graphe. Tests ----- >>> g1 = Graphe('graphe1') >>> n1 = Noeud('n1') >>> n2 = Noeud('n2') >>> n3 = Noeud('n3') >>> g1.addNoeud(n1) >>> g1.addNoeud(n2) >>> g1.addNoeud(n3) >>> for noeud in g1.getNoeuds(): ... print(noeud) ... Noeud(nom=n1, voisins=[], nbArcs=0, capital=0) Noeud(nom=n2, voisins=[], nbArcs=0, capital=0) Noeud(nom=n3, voisins=[], nbArcs=0, capital=0) """ pass return self.noeuds
def getNom(self) ‑> str
-
Renvoie le nom du graphe.
Tests
>>> g1 = Graphe('graphe1') >>> g1.getNom() 'graphe1'
Expand source code
def getNom(self) -> str: """Renvoie le nom du graphe. Tests ----- >>> g1 = Graphe('graphe1') >>> g1.getNom() 'graphe1' """ pass return self.nom
def hasArc(self) ‑> bool
-
Renvoie un booléen selon que la propriété arcs de l'instance de la classe Graphe est une liste vide ou non.
Tests
>>> g1 = Graphe('graphe1') >>> n1,n2 = Noeud('n1'),Noeud('n2') >>> arc = Arc('n1n2',n1,n2,1) >>> g1.hasArc() False >>> g1.addNoeud(n1) >>> g1.addNoeud(n2) >>> g1.addArc(arc) >>> g1.hasArc() True
Expand source code
def hasArc(self) -> bool: """Renvoie un booléen selon que la propriété arcs de l'instance de la classe Graphe est une liste vide ou non. Tests ----- >>> g1 = Graphe('graphe1') >>> n1,n2 = Noeud('n1'),Noeud('n2') >>> arc = Arc('n1n2',n1,n2,1) >>> g1.hasArc() False >>> g1.addNoeud(n1) >>> g1.addNoeud(n2) >>> g1.addArc(arc) >>> g1.hasArc() True """ # noqa : E501 pass hasArc = False if self.getArcs(): hasArc = True return hasArc
def hasArcInverse(self, arcCandidat: models.arc.Arc) ‑> bool
-
Renvoie un booléen selon que le graphe contient un arc inverse de arcCandidat ou pas.
Paramètres
arcCandidat : Arc - L'arc dont on veut savoir si il a un arc inverse.
Remarques
- L'arc inverse n'est pas forcément un arc inverse dans le sens que l'arc d'un cycle de Klein pour être un arc inverse introduit au debut de l'algorithme.
- On suppose que le graphe est simplifié au préalable afin qu'il n'y ait qu'un seul arc entre deux noeuds dans un sens donné.
Tests
>>> g = Graphe('graphe') >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E') >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1) >>> BA = Arc('B->A',B,A,1) >>> CB = Arc('C->B',C,B,1) >>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1) >>> EA = Arc('E->A',E,A,1) >>> g.setNoeuds([A,B,C,D,E]) >>> g.setArcs([AB,AC,AD,BA,CB,DE,DB,EA]) >>> g.hasArcInverse(AB) False >>> # On ajoute tous les arcs inverses au graphe >>> g.addArcsInversesNuls() >>> g.hasArcInverse(AB) True
Expand source code
def hasArcInverse(self, arcCandidat: Arc) -> bool: """Renvoie un booléen selon que le graphe contient un arc inverse de arcCandidat ou pas. Paramètres ---------- arcCandidat : Arc - L'arc dont on veut savoir si il a un arc inverse. Remarques --------- - L'arc inverse n'est pas forcément un arc inverse dans le sens que l'arc d'un cycle de Klein pour être un arc inverse introduit au debut de l'algorithme. - On suppose que le graphe est simplifié au préalable afin qu'il n'y ait qu'un seul arc entre deux noeuds dans un sens donné. Tests ----- >>> g = Graphe('graphe') >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E') >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1) >>> BA = Arc('B->A',B,A,1) >>> CB = Arc('C->B',C,B,1) >>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1) >>> EA = Arc('E->A',E,A,1) >>> g.setNoeuds([A,B,C,D,E]) >>> g.setArcs([AB,AC,AD,BA,CB,DE,DB,EA]) >>> g.hasArcInverse(AB) False >>> # On ajoute tous les arcs inverses au graphe >>> g.addArcsInversesNuls() >>> g.hasArcInverse(AB) True """ pass assert isinstance(arcCandidat, Arc), "arcCandidat doit être un instance de la classe Arc." # noqa : E501 boolDeSortie = False if not arcCandidat.getEstInverse(): for arc in self.getArcsInverses(): if (arcCandidat.getNoeudDebut() == arc.getNoeudFin() and arcCandidat.getNoeudFin() == arc.getNoeudDebut()): boolDeSortie = True if arcCandidat.getEstInverse(): for arc in self.getArcsDirects(): if (arcCandidat.getNoeudDebut() == arc.getNoeudFin() and arcCandidat.getNoeudFin() == arc.getNoeudDebut()): boolDeSortie = True return boolDeSortie
def hasCycle(self) ‑> None
-
Renvoie un booléen selon que le graphe a encore au moins un cycle.
Tests
>>> g = Graphe('graphe') >>> A,B,C,D,E = Noeud('0'),Noeud('1'),Noeud('2'),Noeud('3'),Noeud('4') >>> AB,AC,AD = Arc('0->1',A,B,1),Arc('0->2',A,C,1),Arc('0->3',A,D,1) >>> CB = Arc('2->1',C,B,1) >>> DE = Arc('3->4',D,E,1) >>> EA = Arc('4->0',E,A,1) >>> g.setNoeuds([A,B,C,D,E]) >>> g.setArcs([AB,AC,AD,CB,DE,EA]) >>> print(g) Graphe(nom=graphe, noeuds=['0', '1', '2', '3', '4'], arcs=['0->1', '0->2', '0->3', '2->1', '3->4', '4->0']) >>> # Ce graphe contient un cycle à partir des noeuds A,D ou E : A->D->E->A >>> g.hasCycle() True >>> g1 = Graphe('graphe1') >>> g1.setNoeuds([A,B,C,D,E]) >>> g1.setArcs([AB,AC,AD,CB,EA]) >>> print(g1) Graphe(nom=graphe1, noeuds=['0', '1', '2', '3', '4'], arcs=['0->1', '0->2', '0->3', '2->1', '4->0']) >>> # Ce graphe ne contient pas de cycle >>> g1.hasCycle() False
Expand source code
def hasCycle(self) -> None: """Renvoie un booléen selon que le graphe a encore au moins un cycle. Tests ----- >>> g = Graphe('graphe') >>> A,B,C,D,E = Noeud('0'),Noeud('1'),Noeud('2'),Noeud('3'),Noeud('4') >>> AB,AC,AD = Arc('0->1',A,B,1),Arc('0->2',A,C,1),Arc('0->3',A,D,1) >>> CB = Arc('2->1',C,B,1) >>> DE = Arc('3->4',D,E,1) >>> EA = Arc('4->0',E,A,1) >>> g.setNoeuds([A,B,C,D,E]) >>> g.setArcs([AB,AC,AD,CB,DE,EA]) >>> print(g) Graphe(nom=graphe, noeuds=['0', '1', '2', '3', '4'], arcs=['0->1', '0->2', '0->3', '2->1', '3->4', '4->0']) >>> # Ce graphe contient un cycle à partir des noeuds A,D ou E : A->D->E->A >>> g.hasCycle() True >>> g1 = Graphe('graphe1') >>> g1.setNoeuds([A,B,C,D,E]) >>> g1.setArcs([AB,AC,AD,CB,EA]) >>> print(g1) Graphe(nom=graphe1, noeuds=['0', '1', '2', '3', '4'], arcs=['0->1', '0->2', '0->3', '2->1', '4->0']) >>> # Ce graphe ne contient pas de cycle >>> g1.hasCycle() False """ # noqa : E501 pass boolDeSortie = False index = 0 noeudsDuGraphe = self.getNoeuds() while (not self.hasCycleFromNoeud(noeudsDuGraphe[index]) and index < len(noeudsDuGraphe)-1): index += 1 if index < len(noeudsDuGraphe)-1: boolDeSortie = True return boolDeSortie
def hasCycleFromNoeud(self, noeudCandidat: models.noeud.Noeud) ‑> bool
-
Renvoie un booléen selon que le graphe contient un cycle à partir du noeudCandidat ou non.
Paramètres
noeudCandidat : Noeud - Un noeud du graphe.
Tests
>>> g = Graphe('graphe') >>> A,B,C,D,E = Noeud('0'),Noeud('1'),Noeud('2'),Noeud('3'),Noeud('4') >>> AB,AC,AD = Arc('0->1',A,B,1),Arc('0->2',A,C,1),Arc('0->3',A,D,1) >>> CB = Arc('2->1',C,B,1) >>> DE = Arc('3->4',D,E,1) >>> EA = Arc('4->0',E,A,1) >>> g.setNoeuds([A,B,C,D,E]) >>> g.setArcs([AB,AC,AD,CB,DE,EA]) >>> print(g) Graphe(nom=graphe, noeuds=['0', '1', '2', '3', '4'], arcs=['0->1', '0->2', '0->3', '2->1', '3->4', '4->0']) >>> # Ce graphe contient un cycle à partir des noeuds A,D ou E : A->D->E->A >>> print(g.hasCycleFromNoeud(A)) True >>> print(g.hasCycleFromNoeud(B)) False >>> print(g.hasCycleFromNoeud(C)) False >>> print(g.hasCycleFromNoeud(D)) True >>> print(g.hasCycleFromNoeud(E)) True
Expand source code
def hasCycleFromNoeud(self, noeudCandidat: Noeud) -> bool: """Renvoie un booléen selon que le graphe contient un cycle à partir du noeudCandidat ou non. Paramètres ---------- noeudCandidat : Noeud - Un noeud du graphe. Tests ----- >>> g = Graphe('graphe') >>> A,B,C,D,E = Noeud('0'),Noeud('1'),Noeud('2'),Noeud('3'),Noeud('4') >>> AB,AC,AD = Arc('0->1',A,B,1),Arc('0->2',A,C,1),Arc('0->3',A,D,1) >>> CB = Arc('2->1',C,B,1) >>> DE = Arc('3->4',D,E,1) >>> EA = Arc('4->0',E,A,1) >>> g.setNoeuds([A,B,C,D,E]) >>> g.setArcs([AB,AC,AD,CB,DE,EA]) >>> print(g) Graphe(nom=graphe, noeuds=['0', '1', '2', '3', '4'], arcs=['0->1', '0->2', '0->3', '2->1', '3->4', '4->0']) >>> # Ce graphe contient un cycle à partir des noeuds A,D ou E : A->D->E->A >>> print(g.hasCycleFromNoeud(A)) True >>> print(g.hasCycleFromNoeud(B)) False >>> print(g.hasCycleFromNoeud(C)) False >>> print(g.hasCycleFromNoeud(D)) True >>> print(g.hasCycleFromNoeud(E)) True """ # noqa : E501 pass assert isinstance(noeudCandidat, Noeud), "Le noeudCandidat doit être un instance de la classe Noeud." # noqa : E501 # Il suffit que l'un des creanciers du noeudCandidat # puisse accéder à nouveau à noeudCandidat boolDeSortie = False index = 1 noeudsAccessibles = self.parcoursEnProfondeurNoeuds(noeudCandidat) for index in range(1, len(noeudsAccessibles)): if noeudCandidat in self.parcoursEnProfondeurNoeuds(noeudsAccessibles[index]): # noqa : E501 boolDeSortie = True return boolDeSortie
def hasNoeud(self) ‑> bool
-
Renvoie un booléen selon que la propriété noeuds de l'instance de la classe Graphe est une liste vide ou non.
Tests
>>> g1 = Graphe('graphe1') >>> n1 = Noeud('n1') >>> g1.hasNoeud() False >>> g1.addNoeud(n1) >>> g1.hasNoeud() True
Expand source code
def hasNoeud(self) -> bool: """Renvoie un booléen selon que la propriété noeuds de l'instance de la classe Graphe est une liste vide ou non. Tests ----- >>> g1 = Graphe('graphe1') >>> n1 = Noeud('n1') >>> g1.hasNoeud() False >>> g1.addNoeud(n1) >>> g1.hasNoeud() True """ # noqa : E501 pass hasNoeud = False if self.getNoeuds(): hasNoeud = True return hasNoeud
def isArcInGraphe(self, arcCandidat: models.arc.Arc) ‑> bool
-
Renvoie un booléen selon que l'instance de Arc est dans le graphe ou non.
Paramètres
arcCandidat : Arc - Une instance de la classe Arc dont on veut savoir si elle appartient au graphe.
Tests
>>> g1 = Graphe('graphe1') >>> n1,n2,n3 = Noeud('n1'),Noeud('n2'),Noeud('n3') >>> g1.setNoeuds([n1,n2,n3]) >>> a1,a2 = Arc('n1n2',n1,n2,1), Arc('n1n3',n1,n3,2) >>> g1.addArc(a1) >>> g1.isArcInGraphe(a1) True >>> g1.isArcInGraphe(a2) False
Expand source code
def isArcInGraphe(self, arcCandidat: Arc) -> bool: """Renvoie un booléen selon que l'instance de Arc est dans le graphe ou non. Paramètres ---------- arcCandidat : Arc - Une instance de la classe Arc dont on veut savoir si elle appartient au graphe. Tests ----- >>> g1 = Graphe('graphe1') >>> n1,n2,n3 = Noeud('n1'),Noeud('n2'),Noeud('n3') >>> g1.setNoeuds([n1,n2,n3]) >>> a1,a2 = Arc('n1n2',n1,n2,1), Arc('n1n3',n1,n3,2) >>> g1.addArc(a1) >>> g1.isArcInGraphe(a1) True >>> g1.isArcInGraphe(a2) False """ # noqa : E501 pass assert isinstance(arcCandidat, Arc), "arcCandidat doit être un instance de la classe Arc." # noqa : E501 isArcInGraphe = False if arcCandidat in self.getArcs(): isArcInGraphe = True return isArcInGraphe
def isNoeudInGraphe(self, noeudCandidat: models.noeud.Noeud) ‑> bool
-
Renvoie un booléen selon que l'instance de Noeud est dans le graphe ou non.
Paramètres
noeudCandidat : Noeud - Une instance de la classe Noeud dont on veut savoir si elle appartient au graphe.
Tests
>>> g1 = Graphe('graphe1') >>> n1,n2 = Noeud('n1'),Noeud('n2') >>> g1.addNoeud(n1) >>> g1.isNoeudInGraphe(n1) True >>> g1.isNoeudInGraphe(n2) False
Expand source code
def isNoeudInGraphe(self, noeudCandidat: Noeud) -> bool: """Renvoie un booléen selon que l'instance de Noeud est dans le graphe ou non. Paramètres ---------- noeudCandidat : Noeud - Une instance de la classe Noeud dont on veut savoir si elle appartient au graphe. Tests ----- >>> g1 = Graphe('graphe1') >>> n1,n2 = Noeud('n1'),Noeud('n2') >>> g1.addNoeud(n1) >>> g1.isNoeudInGraphe(n1) True >>> g1.isNoeudInGraphe(n2) False """ # noqa : E501 pass assert isinstance(noeudCandidat, Noeud), "Le noeudCandidat doit être un instance de la classe Noeud." # noqa : E501 isNoeudInGraphe = False if noeudCandidat in self.getNoeuds(): isNoeudInGraphe = True return isNoeudInGraphe
def kleinAllArcsCycles(self) ‑> list
-
Renvoie tous les cycles d'un graphe sans doublons.
Remarques
- Notamment deux cycles sont considérés comme identiques s'ils contiennent globalement les mêmes arcs.
Tests
>>> A, B, C = Noeud('A'), Noeud('B'), Noeud('C') >>> D, E, F = Noeud('D'), Noeud('E'), Noeud('F') >>> kleinAB, kleinBC = Arc('A-4->B', A, B, 4), Arc('B-6->C', B, C, 6) >>> kleinCD, kleinDE = Arc('C-5->D', C, D, 5), Arc('D-4->E', D, E, 4) >>> kleinEF, kleinFA = Arc('E-7->F', E, F, 7), Arc('F-5->A', F, A, 5) >>> kleinAE, kleinBE = Arc('A-2->E', A, E, 2), Arc('B-2->E', B, E, 2) >>> kleinDB, kleinFC = Arc('D-2->B', D, B, 2), Arc('F-3->C', F, C, 3) >>> G3 = Graphe('graphe3') >>> G3.setNoeuds([A, B, C, D, E, F]) >>> G3.setArcs([kleinAB, kleinBC, kleinCD, kleinDE, kleinEF]) >>> G3.addArcs([kleinFA, kleinAE, kleinBE, kleinDB, kleinFC]) >>> # Ce graphe compte 25 cycles si on n'élimine pas les doublons >>> # Mais 6 cycles distincts >>> print(len(G3.kleinAllArcsCycles())) 6 >>> for cycle in G3.kleinAllArcsCycles(): ... print('=== cycle unique ===') ... for arc in cycle: ... print(arc.getNom()) ... === cycle unique === A-4->B B-6->C C-5->D D-4->E E-7->F F-5->A === cycle unique === A-4->B B-2->E E-7->F F-5->A === cycle unique === A-2->E E-7->F F-5->A === cycle unique === B-6->C C-5->D D-2->B === cycle unique === B-2->E E-7->F F-3->C C-5->D D-2->B === cycle unique === C-5->D D-4->E E-7->F F-3->C
Expand source code
def kleinAllArcsCycles(self) -> list: """Renvoie tous les cycles d'un graphe sans doublons. Remarques --------- - Notamment deux cycles sont considérés comme identiques s'ils contiennent globalement les mêmes arcs. Tests ----- >>> A, B, C = Noeud('A'), Noeud('B'), Noeud('C') >>> D, E, F = Noeud('D'), Noeud('E'), Noeud('F') >>> kleinAB, kleinBC = Arc('A-4->B', A, B, 4), Arc('B-6->C', B, C, 6) >>> kleinCD, kleinDE = Arc('C-5->D', C, D, 5), Arc('D-4->E', D, E, 4) >>> kleinEF, kleinFA = Arc('E-7->F', E, F, 7), Arc('F-5->A', F, A, 5) >>> kleinAE, kleinBE = Arc('A-2->E', A, E, 2), Arc('B-2->E', B, E, 2) >>> kleinDB, kleinFC = Arc('D-2->B', D, B, 2), Arc('F-3->C', F, C, 3) >>> G3 = Graphe('graphe3') >>> G3.setNoeuds([A, B, C, D, E, F]) >>> G3.setArcs([kleinAB, kleinBC, kleinCD, kleinDE, kleinEF]) >>> G3.addArcs([kleinFA, kleinAE, kleinBE, kleinDB, kleinFC]) >>> # Ce graphe compte 25 cycles si on n'élimine pas les doublons >>> # Mais 6 cycles distincts >>> print(len(G3.kleinAllArcsCycles())) 6 >>> for cycle in G3.kleinAllArcsCycles(): ... print('=== cycle unique ===') ... for arc in cycle: ... print(arc.getNom()) ... === cycle unique === A-4->B B-6->C C-5->D D-4->E E-7->F F-5->A === cycle unique === A-4->B B-2->E E-7->F F-5->A === cycle unique === A-2->E E-7->F F-5->A === cycle unique === B-6->C C-5->D D-2->B === cycle unique === B-2->E E-7->F F-3->C C-5->D D-2->B === cycle unique === C-5->D D-4->E E-7->F F-3->C """ pass # On récupère tous les noeuds noeuds = self.getNoeuds() # On récupère les cycles au fur et à mesure cyclesSansDoublons = [] # On initialise avec tous les cycles du premier noeud for cycle in self.kleinAllArcsCyclesFromNoeud(noeuds[0]): cyclesSansDoublons.append(cycle) # Pour chaque noeuds for noeud in noeuds: # Pour chaque cycle partant du noeud for cycle in self.kleinAllArcsCyclesFromNoeud(noeud): toAdd = True for cycleUnique in cyclesSansDoublons: if collections.Counter(cycle) == collections.Counter(cycleUnique): # noqa : E501 toAdd = False if toAdd: cyclesSansDoublons.append(cycle) return cyclesSansDoublons
def kleinAllArcsCyclesFromNoeud(self, noeudDepart: models.noeud.Noeud) ‑> list
-
Renvoie une liste de liste d'arcs représentant un cycle de Klein valide s'il en existe
Renvoie [] sinon.
Paramètres
- noeudDepart : Noeud - Le noeud de départ des arcs des cycles.
Remarques
- Le graphe constitué par les arcs directs doit être simplifié.
- La méthode kleinAllArcsWays() exclut déjà les chemin passant deux fois par le même noeud.
- On va ici encore exclure certains chemins cycliques à savoir :
- ceux qui n'ont que deux arcs qui sont inverses l'un de l'autre
- ceux qui n'ont que des arcs inverses
- ceux qui ont un poids algébrique négatif.
- Le poids algébrique est obtenu en ajouter le poids des arcs directs et en enlevant le poids des arcs inverses.
Tests
>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3) >>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1) >>> g = Graphe('graphe') >>> g.setNoeuds([A,B,C]) >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A']) >>> g.estSimplifie() False >>> g.simplification() >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A']) >>> g.estSimplifie() True >>> # Il y a 2 cycles partants de A dans ce graphe >>> # Ce sont aussi des cycles de Klein >>> for cycle in g.kleinAllArcsCyclesFromNoeud(A): ... print("=== cycle ===") ... for arc in cycle: ... print(arc.getNom()) ... === cycle === A-7->B B-5->C C-9->A === cycle === A-3->C C-9->A >>> # On ajoute les arcs inverses manuellement pour les tests >>> BAi, CAi,CBi,ACi = Arc('B-0->A(i)',B,A,0,True),Arc('C-1->A(i)',C,A,1,True),Arc('C-2->B(i)',C,B,2,True),Arc('A-3->C(i)',A,C,3,True) >>> g.addArcs([BAi,CAi,CBi,ACi]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A', 'B-0->A(i)', 'C-1->A(i)', 'C-2->B(i)', 'A-3->C(i)']) >>> for cycle in g.kleinAllArcsCyclesFromNoeud(A): ... print("=== cycle ===") ... for arc in cycle: ... print(arc.getNom()) === cycle === A-7->B B-5->C C-9->A === cycle === A-7->B B-5->C C-1->A(i) === cycle === A-3->C C-9->A >>> # Tests sur un autre graphe >>> D,E,F,G = Noeud('D'),Noeud('E'),Noeud('F'),Noeud('G') >>> DE,EF,FG,FD = Arc('D-1->E',D,E,1),Arc('E-2->F',E,F,2),Arc('F-3->G',F,G,3),Arc('F-4->D',F,D,4) >>> # On ajoute un arc inverse manuellement >>> GFi = Arc('G-3->F(i)',G,F,3,True) >>> g1 = Graphe('graphe1') >>> g1.setNoeuds([D,E,F,G]) >>> g1.setArcs([DE,EF,FG,FD,GFi]) >>> # Ne présente qu'un cycle DEFD, >>> # Le chemin DEFGFD est exclu par la méthode kleinAllArcsWays() >>> for cycle in g1.kleinAllArcsCyclesFromNoeud(D): ... print("=== cycle ===") ... for arc in cycle: ... print(arc.getNom()) ... === cycle === D-1->E E-2->F F-4->D >>> # Quelques arcs en plus qui pourraient poser problème >>> FE,ED = Arc('F-5->E',F,E,5),Arc('E-6->D',E,D,6) >>> FEi,EFi,DEi,EDi = Arc('F-2->E(i)',F,E,2,True),Arc('E-5->F(i)',E,F,5,True),Arc('D-6->E(i)',D,E,6,True),Arc('E-1->D(i)',E,D,1,True) >>> g1.addArcs([FE,ED,FEi,EFi,DEi,EDi]) >>> # Il y a 2 cycles valides à partir de D >>> for cycle in g1.kleinAllArcsCyclesFromNoeud(D): ... print("=== cycle ===") ... for arc in cycle: ... print(arc.getNom()) ... === cycle === D-1->E E-2->F F-4->D === cycle === D-1->E E-6->D
Expand source code
def kleinAllArcsCyclesFromNoeud(self, noeudDepart: Noeud) -> list: """Renvoie une liste de liste d'arcs représentant un cycle de Klein valide s'il en existe Renvoie [] sinon. Paramètres ---------- - noeudDepart : Noeud - Le noeud de départ des arcs des cycles. Remarques --------- - Le graphe constitué par les arcs directs doit être simplifié. - La méthode kleinAllArcsWays() exclut déjà les chemin passant deux fois par le même noeud. - On va ici encore exclure certains chemins cycliques à savoir : - ceux qui n'ont que deux arcs qui sont inverses l'un de l'autre - ceux qui n'ont que des arcs inverses - ceux qui ont un poids algébrique négatif. - Le poids algébrique est obtenu en ajouter le poids des arcs directs et en enlevant le poids des arcs inverses. Tests ----- >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3) >>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1) >>> g = Graphe('graphe') >>> g.setNoeuds([A,B,C]) >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A']) >>> g.estSimplifie() False >>> g.simplification() >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A']) >>> g.estSimplifie() True >>> # Il y a 2 cycles partants de A dans ce graphe >>> # Ce sont aussi des cycles de Klein >>> for cycle in g.kleinAllArcsCyclesFromNoeud(A): ... print("=== cycle ===") ... for arc in cycle: ... print(arc.getNom()) ... === cycle === A-7->B B-5->C C-9->A === cycle === A-3->C C-9->A >>> # On ajoute les arcs inverses manuellement pour les tests >>> BAi, CAi,CBi,ACi = Arc('B-0->A(i)',B,A,0,True),Arc('C-1->A(i)',C,A,1,True),Arc('C-2->B(i)',C,B,2,True),Arc('A-3->C(i)',A,C,3,True) >>> g.addArcs([BAi,CAi,CBi,ACi]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A', 'B-0->A(i)', 'C-1->A(i)', 'C-2->B(i)', 'A-3->C(i)']) >>> for cycle in g.kleinAllArcsCyclesFromNoeud(A): ... print("=== cycle ===") ... for arc in cycle: ... print(arc.getNom()) === cycle === A-7->B B-5->C C-9->A === cycle === A-7->B B-5->C C-1->A(i) === cycle === A-3->C C-9->A >>> # Tests sur un autre graphe >>> D,E,F,G = Noeud('D'),Noeud('E'),Noeud('F'),Noeud('G') >>> DE,EF,FG,FD = Arc('D-1->E',D,E,1),Arc('E-2->F',E,F,2),Arc('F-3->G',F,G,3),Arc('F-4->D',F,D,4) >>> # On ajoute un arc inverse manuellement >>> GFi = Arc('G-3->F(i)',G,F,3,True) >>> g1 = Graphe('graphe1') >>> g1.setNoeuds([D,E,F,G]) >>> g1.setArcs([DE,EF,FG,FD,GFi]) >>> # Ne présente qu'un cycle DEFD, >>> # Le chemin DEFGFD est exclu par la méthode kleinAllArcsWays() >>> for cycle in g1.kleinAllArcsCyclesFromNoeud(D): ... print("=== cycle ===") ... for arc in cycle: ... print(arc.getNom()) ... === cycle === D-1->E E-2->F F-4->D >>> # Quelques arcs en plus qui pourraient poser problème >>> FE,ED = Arc('F-5->E',F,E,5),Arc('E-6->D',E,D,6) >>> FEi,EFi,DEi,EDi = Arc('F-2->E(i)',F,E,2,True),Arc('E-5->F(i)',E,F,5,True),Arc('D-6->E(i)',D,E,6,True),Arc('E-1->D(i)',E,D,1,True) >>> g1.addArcs([FE,ED,FEi,EFi,DEi,EDi]) >>> # Il y a 2 cycles valides à partir de D >>> for cycle in g1.kleinAllArcsCyclesFromNoeud(D): ... print("=== cycle ===") ... for arc in cycle: ... print(arc.getNom()) ... === cycle === D-1->E E-2->F F-4->D === cycle === D-1->E E-6->D """ # noqa : E501 pass # On récupère tous les chemins cycliques allArcsKleinCycles = [] if self.kleinHasCycleFromNoeud(noeudDepart): noeudsPossibles = [] for noeud in self.getCreanciers(noeudDepart): if (self.kleinAllArcsWays(noeud, noeudDepart) and noeud not in noeudsPossibles): noeudsPossibles.append(noeud) # Pour tous les noeuds qui permettent de revenir à noeudDepart for noeudPossible in noeudsPossibles: arcsKleinCycle = [] # Pour tous les chemins entre un noeudPossible et noeudDepart suiteChemin = self.kleinAllArcsWays(noeudPossible, noeudDepart) # noqa : E501 for chemin in suiteChemin: # pour tous les arcs qui permettent de joindre noeudDepart # et noeudPossible debutChemin = self.kleinArcsBetween(noeudDepart, noeudPossible) # noqa : E501 for premierArc in debutChemin: # On remet à zéro arcsKleinCycle = [] # On ajoute le premier arc arcsKleinCycle.extend([premierArc]) # On ajoute le reste du chemin arcsKleinCycle.extend(chemin) # On évite les doublons if arcsKleinCycle not in allArcsKleinCycles: allArcsKleinCycles.append(arcsKleinCycle) # Il faut encore nettoyer de certains chemins : # -> ceux qui n'ont que deux arcs qui sont inverses # l'un de l'autre # -> ceux qui n'ont que des arcs inverses # -> ceux qui ont un poids algébrique négatif. # ======== Remarque # On a besoin d'un variable globale pour éviter l'erreur # UnboundLocalError: local variable referenced before assignment python # noqa : E501 # ======== global allArcsKleinCyclesCleaned allArcsKleinCyclesCleaned = [] for arcsKleinCycle in allArcsKleinCycles: cycleToKeep = True # ceux qui n'ont que deux arcs qui sont inverses # l'un de l'autre if len(arcsKleinCycle) == 2: arc0 = arcsKleinCycle[0] arc1 = arcsKleinCycle[1] if self.hasArcInverse(arc1): if arc0 == self.getArcInverse(arc1): cycleToKeep = False if self.hasArcInverse(arc0): if arc1 == self.getArcInverse(arc0): cycleToKeep = False # ceux qui n'ont que des arcs inverses onlyArcsInverses = True for arc in arcsKleinCycle: if not arc.getEstInverse(): onlyArcsInverses = False if onlyArcsInverses: cycleToKeep = False # ceux qui ont un poids algébrique négatif cycleWeight = 0 for arc in arcsKleinCycle: if arc.getEstInverse(): cycleWeight -= arc.getPoids() if not arc.getEstInverse(): cycleWeight += arc.getPoids() if cycleWeight <= 0: cycleToKeep = False if cycleToKeep: allArcsKleinCyclesCleaned.append(arcsKleinCycle) return allArcsKleinCyclesCleaned
def kleinAllArcsWays(self, noeudDepart: models.noeud.Noeud, noeudArrivee: models.noeud.Noeud) ‑> list
-
Renvoie une liste de tuples :
- d'arcs de poids non nul
- et/ou d'arcs inverses de poids non nul
- ne passant pas deux fois par le même noeud contenant un chemin entre noeudDepart et noeudArrivee s'il en existe. Renvoie [] sinon.
Paramètres
- noeudDepart : Noeud - Le noeud de départ des arcs des chemins.
- noeudArrivee : Noeud - Le noeud de d'arrivée des arcs des chemins.
Remarques
- Ne trouve pas les cycles.
- Méthode utile pour l'algorithme de Klein.
- On ne peut pas supprimer de chemins ici en amont de la recherche de cycles
noeudDepart : Noeud - noeud de départ noeudArrivee : Noeud - noeud d'arrivée
Tests
>>> g = Graphe('graphe') >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E') >>> AB1,AC,AD = Arc('A->B(1)',A,B,0),Arc('A->C',A,C,1),Arc('A->D',A,D,1) >>> ABi = Arc('A->B(i)',A,B,1,True) >>> BA1 = Arc('B->A(1)',B,A,0) >>> BAi = Arc('B->A(i)',B,A,2,True) >>> CB = Arc('C->B',C,B,1) >>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1) >>> EA,EB = Arc('E->A',E,A,1),Arc('E->B',E,B,1) >>> g.setNoeuds([A,B,C,D,E]) >>> g.setArcs([AB1,ABi,AC,AD,BA1,BAi,CB,DE,DB,EA,EB]) >>> g.estSimplifie() True >>> # Deux chemins de Klein de B à C >>> # Mais on ne veut que les chemins : >>> # --> qui ont des arcs directs de poids non nul >>> # --> et/ou des arcs inverses de poids non nul >>> # Cela n'en laisse qu'un >>> for chemin in g.kleinAllArcsWays(B,C): ... print("=== chemin de Klein ===") ... for arc in chemin: ... print(arc.getNom()) ... === chemin de Klein === B->A(i) A->C >>> # Dans ce graphe cinq chemins sont possibles entre A et B >>> # Mais on ne veut que les chemins : >>> # --> qui ont des arcs directs de poids non nul >>> # --> et/ou des arcs inverses de poids non nul >>> # Cela n'en laisse que quatre >>> for chemin in g.kleinAllArcsWays(A,B): ... print("=== chemin de Klein ===") ... for arc in chemin: ... print(arc.getNom()) === chemin de Klein === A->B(i) === chemin de Klein === A->C C->B === chemin de Klein === A->D D->E E->B === chemin de Klein === A->D D->B >>> # On remet un poids non nul sur les arcs AB1 pour retrouver les cinq chemins >>> AB1.setPoids(1) >>> for chemin in g.kleinAllArcsWays(A,B): ... print("=== chemin de Klein ===") ... for arc in chemin: ... print(arc.getNom()) === chemin de Klein === A->B(1) === chemin de Klein === A->B(i) === chemin de Klein === A->C C->B === chemin de Klein === A->D D->E E->B === chemin de Klein === A->D D->B >>> # Si on mets un poids nul ailleurs que sur le premier arc d'un chemin >>> # Le chemin doit être supprimé des possibles aussi >>> # Cela laisse quatre chemins valides >>> EB.setPoids(0) >>> for chemin in g.kleinAllArcsWays(A,B): ... print("=== chemin de Klein ===") ... for arc in chemin: ... print(arc.getNom()) === chemin de Klein === A->B(1) === chemin de Klein === A->B(i) === chemin de Klein === A->C C->B === chemin de Klein === A->D D->B
Expand source code
def kleinAllArcsWays( self, noeudDepart: Noeud, noeudArrivee: Noeud ) -> list: """Renvoie une liste de tuples : - d'arcs de poids non nul - et/ou d'arcs inverses de poids non nul - ne passant pas deux fois par le même noeud contenant un chemin entre noeudDepart et noeudArrivee s'il en existe. Renvoie [] sinon. Paramètres ---------- - noeudDepart : Noeud - Le noeud de départ des arcs des chemins. - noeudArrivee : Noeud - Le noeud de d'arrivée des arcs des chemins. Remarques --------- - Ne trouve pas les cycles. - Méthode utile pour l'algorithme de Klein. - On ne peut pas supprimer de chemins ici en amont de la recherche de cycles noeudDepart : Noeud - noeud de départ noeudArrivee : Noeud - noeud d'arrivée Tests ----- >>> g = Graphe('graphe') >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E') >>> AB1,AC,AD = Arc('A->B(1)',A,B,0),Arc('A->C',A,C,1),Arc('A->D',A,D,1) >>> ABi = Arc('A->B(i)',A,B,1,True) >>> BA1 = Arc('B->A(1)',B,A,0) >>> BAi = Arc('B->A(i)',B,A,2,True) >>> CB = Arc('C->B',C,B,1) >>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1) >>> EA,EB = Arc('E->A',E,A,1),Arc('E->B',E,B,1) >>> g.setNoeuds([A,B,C,D,E]) >>> g.setArcs([AB1,ABi,AC,AD,BA1,BAi,CB,DE,DB,EA,EB]) >>> g.estSimplifie() True >>> # Deux chemins de Klein de B à C >>> # Mais on ne veut que les chemins : >>> # --> qui ont des arcs directs de poids non nul >>> # --> et/ou des arcs inverses de poids non nul >>> # Cela n'en laisse qu'un >>> for chemin in g.kleinAllArcsWays(B,C): ... print("=== chemin de Klein ===") ... for arc in chemin: ... print(arc.getNom()) ... === chemin de Klein === B->A(i) A->C >>> # Dans ce graphe cinq chemins sont possibles entre A et B >>> # Mais on ne veut que les chemins : >>> # --> qui ont des arcs directs de poids non nul >>> # --> et/ou des arcs inverses de poids non nul >>> # Cela n'en laisse que quatre >>> for chemin in g.kleinAllArcsWays(A,B): ... print("=== chemin de Klein ===") ... for arc in chemin: ... print(arc.getNom()) === chemin de Klein === A->B(i) === chemin de Klein === A->C C->B === chemin de Klein === A->D D->E E->B === chemin de Klein === A->D D->B >>> # On remet un poids non nul sur les arcs AB1 pour retrouver les cinq chemins >>> AB1.setPoids(1) >>> for chemin in g.kleinAllArcsWays(A,B): ... print("=== chemin de Klein ===") ... for arc in chemin: ... print(arc.getNom()) === chemin de Klein === A->B(1) === chemin de Klein === A->B(i) === chemin de Klein === A->C C->B === chemin de Klein === A->D D->E E->B === chemin de Klein === A->D D->B >>> # Si on mets un poids nul ailleurs que sur le premier arc d'un chemin >>> # Le chemin doit être supprimé des possibles aussi >>> # Cela laisse quatre chemins valides >>> EB.setPoids(0) >>> for chemin in g.kleinAllArcsWays(A,B): ... print("=== chemin de Klein ===") ... for arc in chemin: ... print(arc.getNom()) === chemin de Klein === A->B(1) === chemin de Klein === A->B(i) === chemin de Klein === A->C C->B === chemin de Klein === A->D D->B """ # noqa : E501 pass assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud." # noqa : E501 assert isinstance(noeudArrivee, Noeud), "Le noeudArrivee doit être un instance de la classe Noeud." # noqa : E501 assert noeudDepart != noeudArrivee, "noeudDepart et noeudArrivee doivent être distincts." # noqa : E501 assert self.estSimplifie(), "Les arcs directs du graphe doivent être simplifiés, fusionnés s'ils sont parallèles." # noqa : E501 # Si il existe au moins un chemin entre noeudDepart et noeudArrivee # On ne garde que les chemins : # -> dont les arcs ont des poids non nuls # -> dont les arcs inverses ont des poids non nuls # -> ne passant pas deux fois par le même noeud arcsWays = self.getArcsWays(noeudDepart, noeudArrivee) if arcsWays: # les chemins à garder, a priori vide arcsWaysOk = [] for arcsWay in arcsWays: wayToKeep = True noeudsConnus = [noeudDepart] for arc in arcsWay: if (arc.getPoids() == 0 or arc.getNoeudFin() in noeudsConnus): wayToKeep = False if wayToKeep: noeudsConnus.append(arc.getNoeudFin()) noeudsConnus = [] if wayToKeep: arcsWaysOk.append(arcsWay) else: arcsWaysOk = [] return arcsWaysOk
def kleinArcsBetween(self, noeudDepart: models.noeud.Noeud, noeudArrivee: models.noeud.Noeud) ‑> list
-
Renvoie tous les arcs de poids non nul entre noeudDepart et noeudArrivee.
Paramètres
- noeudDepart : Noeud - Le noeud de départ des arcs.
- noeudArrivee : Noeud - Le noeud de d'arrivée des arcs.
Remarque
Méthode utile pour l'algorithme de Klein
Tests
>>> g = Graphe('graphe') >>> A,B,D,E = Noeud('A'),Noeud('B'),Noeud('D'),Noeud('E') >>> AB1,AB2,AB3 = Arc('A->B(1)',A,B,0),Arc('A->B(2)',A,B,1),Arc('A->B(3)',A,B,1) >>> DEg = Arc('D->E',D,E,1) >>> EAg = Arc('E->A',E,A,1) >>> g.setNoeuds([A,B,D,E]) >>> g.setArcs([AB1,AB2,AB3,DEg,EAg]) >>> for a in g.kleinArcsBetween(A,B): ... print(a.getNom()) ... A->B(2) A->B(3) >>> # Aucun arc de D à A >>> for a in g.kleinArcsBetween(D,A): ... print(a.getNom()) ... >>> g1 = Graphe('graphe1') >>> A1,B1,C1,D1,E1 = Noeud('A1'),Noeud('B1'),Noeud('C1'),Noeud('D1'),Noeud('E1') >>> AB,AC,AD = Arc('A1->B1',A1,B1,1),Arc('A1->C1',A1,C1,1),Arc('A1->D1',A1,D1,1) >>> ABbis = Arc('A1->B1(2)',A1,B1,2) >>> CB = Arc('C1->B1',C1,B1,1) >>> DE,DB = Arc('D1->E1',D1,E1,1),Arc('D1->B1',D1,B1,1) >>> EA = Arc('E1->A1',E1,A1,1) >>> EB = Arc('E1->B1',E1,B1,1) >>> g1.setNoeuds([A1,B1,C1,D1,E1]) >>> g1.setArcs([AB,AC,AD,ABbis,CB,DE,DB,EA,EB]) >>> # print(g1.kleinArcsBetween(A1,B1)) >>> for a in g1.kleinArcsBetween(A1,B1): ... print(a.getNom()) ... A1->B1 A1->B1(2)
Expand source code
def kleinArcsBetween( self, noeudDepart: Noeud, noeudArrivee: Noeud ) -> list: """Renvoie tous les arcs de poids non nul entre noeudDepart et noeudArrivee. Paramètres ---------- - noeudDepart : Noeud - Le noeud de départ des arcs. - noeudArrivee : Noeud - Le noeud de d'arrivée des arcs. Remarque -------- Méthode utile pour l'algorithme de Klein Tests ----- >>> g = Graphe('graphe') >>> A,B,D,E = Noeud('A'),Noeud('B'),Noeud('D'),Noeud('E') >>> AB1,AB2,AB3 = Arc('A->B(1)',A,B,0),Arc('A->B(2)',A,B,1),Arc('A->B(3)',A,B,1) >>> DEg = Arc('D->E',D,E,1) >>> EAg = Arc('E->A',E,A,1) >>> g.setNoeuds([A,B,D,E]) >>> g.setArcs([AB1,AB2,AB3,DEg,EAg]) >>> for a in g.kleinArcsBetween(A,B): ... print(a.getNom()) ... A->B(2) A->B(3) >>> # Aucun arc de D à A >>> for a in g.kleinArcsBetween(D,A): ... print(a.getNom()) ... >>> g1 = Graphe('graphe1') >>> A1,B1,C1,D1,E1 = Noeud('A1'),Noeud('B1'),Noeud('C1'),Noeud('D1'),Noeud('E1') >>> AB,AC,AD = Arc('A1->B1',A1,B1,1),Arc('A1->C1',A1,C1,1),Arc('A1->D1',A1,D1,1) >>> ABbis = Arc('A1->B1(2)',A1,B1,2) >>> CB = Arc('C1->B1',C1,B1,1) >>> DE,DB = Arc('D1->E1',D1,E1,1),Arc('D1->B1',D1,B1,1) >>> EA = Arc('E1->A1',E1,A1,1) >>> EB = Arc('E1->B1',E1,B1,1) >>> g1.setNoeuds([A1,B1,C1,D1,E1]) >>> g1.setArcs([AB,AC,AD,ABbis,CB,DE,DB,EA,EB]) >>> # print(g1.kleinArcsBetween(A1,B1)) >>> for a in g1.kleinArcsBetween(A1,B1): ... print(a.getNom()) ... A1->B1 A1->B1(2) """ # noqa : E501 pass assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud." # noqa : E501 assert isinstance(noeudArrivee, Noeud), "Le noeudArrivee doit être un instance de la classe Noeud." # noqa : E501 arcs = [] for arc in self.getArcs(): if (arc.getNoeudDebut() == noeudDepart and arc.getNoeudFin() == noeudArrivee and arc.getPoids() != 0): arcs.append(arc) return arcs
def kleinArcsCycleFromNoeud(self, noeudDepart: models.noeud.Noeud) ‑> list
-
Renvoie une liste d'arcs représentant un cycle de Klein partant du noeudDepart s'il en existe.
Renvoie [] sinon.
Paramètres
- noeudDepart : Noeud - Le noeud de départ des arcs du cycle.
Remarques
- S'il existe plusieurs cycles à partir d'un noeud, l'algorithme n'en renvoie qu'un seul au hasard.
- La recherche de cycles de Klein sous-entend que le graphe est simplifié, c'est à dire que les arcs parallèles entre deux noeuds soient fucionnés.
- Un cycle ne doit pas passer plusieurs fois par le même noeud
Tests
>>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3) >>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1) >>> g = Graphe('graphe') >>> g.setNoeuds([A,B,C]) >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A']) >>> g.estSimplifie() False >>> g.simplification() >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A']) >>> g.estSimplifie() True >>> # Il y a 2 cycles partants de A dans ce graphe >>> # Ce sont aussi des cycles de Klein >>> # On les récupère tous en fixant la graine >>> random.seed(1) >>> for arc in g.kleinArcsCycleFromNoeud(A): ... print(arc.getNom()) ... A-7->B B-5->C C-9->A >>> random.seed(5) >>> for arc in g.kleinArcsCycleFromNoeud(A): ... print(arc.getNom()) ... A-3->C C-9->A >>> # On ajoute les arcs inverses manuellement pour les tests >>> BAi, CAi,CBi,ACi = Arc('B-0->A(i)',B,A,0,True),Arc('C-1->A(i)',C,A,1,True),Arc('C-2->B(i)',C,B,2,True),Arc('A-3->C(i)',A,C,3,True) >>> g.addArcs([BAi,CAi,CBi,ACi]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A', 'B-0->A(i)', 'C-1->A(i)', 'C-2->B(i)', 'A-3->C(i)']) >>> # En fixant la graine on teste pour vérifier qu'on obtient bien tous les cycles >>> for cycle in g.kleinAllArcsCyclesFromNoeud(A): ... print("=== cycle ===") ... for arc in cycle: ... print(arc.getNom()) ... === cycle === A-7->B B-5->C C-9->A === cycle === A-7->B B-5->C C-1->A(i) === cycle === A-3->C C-9->A >>> random.seed(1) >>> for arc in g.kleinArcsCycleFromNoeud(A): ... print(arc.getNom()) ... A-7->B B-5->C C-9->A >>> random.seed(5) >>> for arc in g.kleinArcsCycleFromNoeud(A): ... print(arc.getNom()) ... A-3->C C-9->A >>> random.seed(7) >>> for arc in g.kleinArcsCycleFromNoeud(A): ... print(arc.getNom()) ... A-7->B B-5->C C-1->A(i)
Expand source code
def kleinArcsCycleFromNoeud(self, noeudDepart: Noeud) -> list: """Renvoie une liste d'arcs représentant un cycle de Klein partant du noeudDepart s'il en existe. Renvoie [] sinon. Paramètres ---------- - noeudDepart : Noeud - Le noeud de départ des arcs du cycle. Remarques --------- - S'il existe plusieurs cycles à partir d'un noeud, l'algorithme n'en renvoie qu'un seul au hasard. - La recherche de cycles de Klein sous-entend que le graphe est simplifié, c'est à dire que les arcs parallèles entre deux noeuds soient fucionnés. - Un cycle ne doit pas passer plusieurs fois par le même noeud Tests ----- >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3) >>> BC5,CA8,CA1 = Arc('B-5->C',B,C,5),Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1) >>> g = Graphe('graphe') >>> g.setNoeuds([A,B,C]) >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-5->B', 'A-2->B', 'A-3->C', 'B-5->C', 'C-8->A', 'C-1->A']) >>> g.estSimplifie() False >>> g.simplification() >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A']) >>> g.estSimplifie() True >>> # Il y a 2 cycles partants de A dans ce graphe >>> # Ce sont aussi des cycles de Klein >>> # On les récupère tous en fixant la graine >>> random.seed(1) >>> for arc in g.kleinArcsCycleFromNoeud(A): ... print(arc.getNom()) ... A-7->B B-5->C C-9->A >>> random.seed(5) >>> for arc in g.kleinArcsCycleFromNoeud(A): ... print(arc.getNom()) ... A-3->C C-9->A >>> # On ajoute les arcs inverses manuellement pour les tests >>> BAi, CAi,CBi,ACi = Arc('B-0->A(i)',B,A,0,True),Arc('C-1->A(i)',C,A,1,True),Arc('C-2->B(i)',C,B,2,True),Arc('A-3->C(i)',A,C,3,True) >>> g.addArcs([BAi,CAi,CBi,ACi]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A', 'B-0->A(i)', 'C-1->A(i)', 'C-2->B(i)', 'A-3->C(i)']) >>> # En fixant la graine on teste pour vérifier qu'on obtient bien tous les cycles >>> for cycle in g.kleinAllArcsCyclesFromNoeud(A): ... print("=== cycle ===") ... for arc in cycle: ... print(arc.getNom()) ... === cycle === A-7->B B-5->C C-9->A === cycle === A-7->B B-5->C C-1->A(i) === cycle === A-3->C C-9->A >>> random.seed(1) >>> for arc in g.kleinArcsCycleFromNoeud(A): ... print(arc.getNom()) ... A-7->B B-5->C C-9->A >>> random.seed(5) >>> for arc in g.kleinArcsCycleFromNoeud(A): ... print(arc.getNom()) ... A-3->C C-9->A >>> random.seed(7) >>> for arc in g.kleinArcsCycleFromNoeud(A): ... print(arc.getNom()) ... A-7->B B-5->C C-1->A(i) """ # noqa : E501 pass assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud." # noqa : E501 arcsKleinCycle = [] if self.kleinHasCycleFromNoeud(noeudDepart): allCycles = self.kleinAllArcsCyclesFromNoeud(noeudDepart) arcsKleinCycle = random.choice(allCycles) return arcsKleinCycle
def kleinArcsWay(self, noeudDepart: models.noeud.Noeud, noeudArrivee: models.noeud.Noeud) ‑> list
-
Renvoie une liste :
- d'arcs de poids non nul
- et/ou d'arcs inverses de poids non nul
- ne passant pas deux fois par le même noeud
constituant un chemin entre noeudDepart et noeudArrivee s'il en existe. Renvoie [] sinon.
Paramètres
- noeudDepart : Noeud - Le noeud de départ des arcs du chemin.
- noeudArrivee : Noeud - Le noeud de d'arrivée des arcs du chemin.
Remarque
Ne trouve pas les cycles.
Méthode utile pour l'algorithme de Klein.noeudDepart : Noeud - noeud de départ noeudArrivee : Noeud - noeud d'arrivée
Tests
>>> g = Graphe('graphe') >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E') >>> AB1,AC,AD = Arc('A->B(1)',A,B,0),Arc('A->C',A,C,1),Arc('A->D',A,D,1) >>> ABi = Arc('A->B(i)',A,B,1,True) >>> BA1 = Arc('B->A(1)',B,A,0) >>> BAi = Arc('B->A(i)',B,A,2,True) >>> CB = Arc('C->B',C,B,1) >>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1) >>> EA,EB = Arc('E->A',E,A,1),Arc('E->B',E,B,1) >>> g.setNoeuds([A,B,C,D,E]) >>> g.setArcs([AB1,ABi,AC,AD,BA1,BAi,CB,DE,DB,EA,EB]) >>> g.estSimplifie() True >>> # Un seul chemin de Klein de B à C >>> for chemin in g.kleinAllArcsWays(B,C): ... print("=== chemin de Klein ===") ... for arc in chemin: ... print(arc.getNom()) ... === chemin de Klein === B->A(i) A->C >>> # Dans ce graphe cinq chemins sont possibles entre A et B >>> # Mais on ne veut que les chemins avec >>> # des arcs directs de poids non nul >>> # et/ou des arcs inverses de poids non nul >>> # Cela n'en laisse que quatre >>> for chemin in g.kleinAllArcsWays(A,B): ... print("=== chemin de Klein ===") ... for arc in chemin: ... print(arc.getNom()) ... === chemin de Klein === A->B(i) === chemin de Klein === A->C C->B === chemin de Klein === A->D D->E E->B === chemin de Klein === A->D D->B >>> # On fixe la graine pour en avoir un au hasard >>> random.seed(1) >>> for arc in g.kleinArcsWay(A,B): ... print(arc.getNom()) A->C C->B >>> # On remet un poids non nul sur les arcs AB1, pour retrouver les cinq chemins >>> AB1.setPoids(1) >>> for chemin in g.kleinAllArcsWays(A,B): ... print("=== chemin de Klein ===") ... for arc in chemin: ... print(arc.getNom()) ... === chemin de Klein === A->B(1) === chemin de Klein === A->B(i) === chemin de Klein === A->C C->B === chemin de Klein === A->D D->E E->B === chemin de Klein === A->D D->B >>> # On fixe la graine pour en avoir un au hasard >>> random.seed(1) >>> for arc in g.kleinArcsWay(A,B): ... print(arc.getNom()) A->B(i) >>> # Si on mets un poids nul ailleurs que sur le premier arc d'un chemin >>> # Le chemin doit être supprimé des possibles aussi >>> # Cela laisse quatre cemins valides >>> EB.setPoids(0) >>> for chemin in g.kleinAllArcsWays(A,B): ... print("=== chemin de Klein ===") ... for arc in chemin: ... print(arc.getNom()) ... === chemin de Klein === A->B(1) === chemin de Klein === A->B(i) === chemin de Klein === A->C C->B === chemin de Klein === A->D D->B >>> # On a fixé la graine pour en avoir un au hasard >>> for arc in g.kleinArcsWay(A,B): ... print(arc.getNom()) A->B(1)
Expand source code
def kleinArcsWay( self, noeudDepart: Noeud, noeudArrivee: Noeud ) -> list: """Renvoie une liste : - d'arcs de poids non nul - et/ou d'arcs inverses de poids non nul - ne passant pas deux fois par le même noeud constituant un chemin entre noeudDepart et noeudArrivee s'il en existe. Renvoie [] sinon. Paramètres ---------- - noeudDepart : Noeud - Le noeud de départ des arcs du chemin. - noeudArrivee : Noeud - Le noeud de d'arrivée des arcs du chemin. Remarque -------- Ne trouve pas les cycles. Méthode utile pour l'algorithme de Klein. noeudDepart : Noeud - noeud de départ noeudArrivee : Noeud - noeud d'arrivée Tests ----- >>> g = Graphe('graphe') >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E') >>> AB1,AC,AD = Arc('A->B(1)',A,B,0),Arc('A->C',A,C,1),Arc('A->D',A,D,1) >>> ABi = Arc('A->B(i)',A,B,1,True) >>> BA1 = Arc('B->A(1)',B,A,0) >>> BAi = Arc('B->A(i)',B,A,2,True) >>> CB = Arc('C->B',C,B,1) >>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1) >>> EA,EB = Arc('E->A',E,A,1),Arc('E->B',E,B,1) >>> g.setNoeuds([A,B,C,D,E]) >>> g.setArcs([AB1,ABi,AC,AD,BA1,BAi,CB,DE,DB,EA,EB]) >>> g.estSimplifie() True >>> # Un seul chemin de Klein de B à C >>> for chemin in g.kleinAllArcsWays(B,C): ... print("=== chemin de Klein ===") ... for arc in chemin: ... print(arc.getNom()) ... === chemin de Klein === B->A(i) A->C >>> # Dans ce graphe cinq chemins sont possibles entre A et B >>> # Mais on ne veut que les chemins avec >>> # des arcs directs de poids non nul >>> # et/ou des arcs inverses de poids non nul >>> # Cela n'en laisse que quatre >>> for chemin in g.kleinAllArcsWays(A,B): ... print("=== chemin de Klein ===") ... for arc in chemin: ... print(arc.getNom()) ... === chemin de Klein === A->B(i) === chemin de Klein === A->C C->B === chemin de Klein === A->D D->E E->B === chemin de Klein === A->D D->B >>> # On fixe la graine pour en avoir un au hasard >>> random.seed(1) >>> for arc in g.kleinArcsWay(A,B): ... print(arc.getNom()) A->C C->B >>> # On remet un poids non nul sur les arcs AB1, pour retrouver les cinq chemins >>> AB1.setPoids(1) >>> for chemin in g.kleinAllArcsWays(A,B): ... print("=== chemin de Klein ===") ... for arc in chemin: ... print(arc.getNom()) ... === chemin de Klein === A->B(1) === chemin de Klein === A->B(i) === chemin de Klein === A->C C->B === chemin de Klein === A->D D->E E->B === chemin de Klein === A->D D->B >>> # On fixe la graine pour en avoir un au hasard >>> random.seed(1) >>> for arc in g.kleinArcsWay(A,B): ... print(arc.getNom()) A->B(i) >>> # Si on mets un poids nul ailleurs que sur le premier arc d'un chemin >>> # Le chemin doit être supprimé des possibles aussi >>> # Cela laisse quatre cemins valides >>> EB.setPoids(0) >>> for chemin in g.kleinAllArcsWays(A,B): ... print("=== chemin de Klein ===") ... for arc in chemin: ... print(arc.getNom()) ... === chemin de Klein === A->B(1) === chemin de Klein === A->B(i) === chemin de Klein === A->C C->B === chemin de Klein === A->D D->B >>> # On a fixé la graine pour en avoir un au hasard >>> for arc in g.kleinArcsWay(A,B): ... print(arc.getNom()) A->B(1) """ # noqa : E501 pass assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud." # noqa : E501 assert isinstance(noeudArrivee, Noeud), "Le noeudArrivee doit être un instance de la classe Noeud." # noqa : E501 assert noeudDepart != noeudArrivee, "noeudDepart et noeudArrivee doivent être distincts." # noqa : E501 assert self.estSimplifie(), "Les arcs directs du graphe doivent être simplifiés, fusionnés s'ils sont parallèles." # noqa : E501 arcsWayOk = [] kleinArcsWays = self.kleinAllArcsWays(noeudDepart, noeudArrivee) if kleinArcsWays: arcsWayOk = random.choice(kleinArcsWays) return arcsWayOk
def kleinBestArcsCycle(self) ‑> list
-
Renvoie le liste d'arcs réduisant au maximum la dette globale d'un graphe.
Remarques
- Il semble qu'en choisissant à chaque étape le cycle qui réduit au maximum la dette globale, la réduction finale ne soit pas la meilleure.
- Cette fonction est donc incorrecte
Tests
>>> A, B, C = Noeud('A'), Noeud('B'), Noeud('C') >>> D, E, F = Noeud('D'), Noeud('E'), Noeud('F') >>> kleinAB, kleinBC = Arc('A-4->B', A, B, 4), Arc('B-6->C', B, C, 6) >>> kleinCD, kleinDE = Arc('C-5->D', C, D, 5), Arc('D-4->E', D, E, 4) >>> kleinEF, kleinFA = Arc('E-7->F', E, F, 7), Arc('F-5->A', F, A, 5) >>> kleinAE, kleinBE = Arc('A-2->E', A, E, 2), Arc('B-2->E', B, E, 2) >>> kleinDB, kleinFC = Arc('D-2->B', D, B, 2), Arc('F-3->C', F, C, 3) >>> G3 = Graphe('graphe3') >>> G3.setNoeuds([A, B, C, D, E, F]) >>> G3.setArcs([kleinAB, kleinBC, kleinCD, kleinDE, kleinEF]) >>> G3.addArcs([kleinFA, kleinAE, kleinBE, kleinDB, kleinFC]) >>> # Ce graphe compte 25 cycles si on n'élimine pas les doublons >>> # Mais 6 cycles distincts >>> print(len(G3.kleinAllArcsCycles())) 6 >>> G3.addArcsInversesNuls() >>> bestCycle1 = G3.kleinBestArcsCycle() >>> for arc in bestCycle1: ... print(arc.getNom()) ... A-4->B B-6->C C-5->D D-4->E E-7->F F-5->A >>> G3.kleinModifierCycle(bestCycle1) >>> bestCycle2 = G3.kleinBestArcsCycle() >>> for arc in bestCycle2: ... print(arc.getNom()) ... B-2->E E-3->F F-3->C C-4->B
Expand source code
def kleinBestArcsCycle(self) -> list: """Renvoie le liste d'arcs réduisant au maximum la dette globale d'un graphe. Remarques --------- - Il semble qu'en choisissant à chaque étape le cycle qui réduit au maximum la dette globale, la réduction finale ne soit pas la meilleure. - Cette fonction est donc incorrecte Tests ----- >>> A, B, C = Noeud('A'), Noeud('B'), Noeud('C') >>> D, E, F = Noeud('D'), Noeud('E'), Noeud('F') >>> kleinAB, kleinBC = Arc('A-4->B', A, B, 4), Arc('B-6->C', B, C, 6) >>> kleinCD, kleinDE = Arc('C-5->D', C, D, 5), Arc('D-4->E', D, E, 4) >>> kleinEF, kleinFA = Arc('E-7->F', E, F, 7), Arc('F-5->A', F, A, 5) >>> kleinAE, kleinBE = Arc('A-2->E', A, E, 2), Arc('B-2->E', B, E, 2) >>> kleinDB, kleinFC = Arc('D-2->B', D, B, 2), Arc('F-3->C', F, C, 3) >>> G3 = Graphe('graphe3') >>> G3.setNoeuds([A, B, C, D, E, F]) >>> G3.setArcs([kleinAB, kleinBC, kleinCD, kleinDE, kleinEF]) >>> G3.addArcs([kleinFA, kleinAE, kleinBE, kleinDB, kleinFC]) >>> # Ce graphe compte 25 cycles si on n'élimine pas les doublons >>> # Mais 6 cycles distincts >>> print(len(G3.kleinAllArcsCycles())) 6 >>> G3.addArcsInversesNuls() >>> bestCycle1 = G3.kleinBestArcsCycle() >>> for arc in bestCycle1: ... print(arc.getNom()) ... A-4->B B-6->C C-5->D D-4->E E-7->F F-5->A >>> G3.kleinModifierCycle(bestCycle1) >>> bestCycle2 = G3.kleinBestArcsCycle() >>> for arc in bestCycle2: ... print(arc.getNom()) ... B-2->E E-3->F F-3->C C-4->B """ pass # On récupère tous les cycles sans doublons allCycles = self.kleinAllArcsCycles() bestCycle = [] reductionDeLaDette = 0 for cycle in allCycles: reductionDuCycle = len(cycle)*self.getMinPoidsFromCycle(cycle) if reductionDuCycle > reductionDeLaDette: reductionDeLaDette = reductionDuCycle bestCycle = [] bestCycle.extend(cycle) return bestCycle
def kleinDeleteArcsInverses(self) ‑> None
-
Supprime tous les arcs inverses temporaires.
Tests
>>> g = Graphe('graphe') >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3) >>> BC5 = Arc('B-5->C',B,C,5) >>> CA8,CA1 = Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1) >>> g.setNoeuds([A,B,C]) >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) >>> # On simplifie le graphe >>> g.simplification() >>> for arc in g.getArcs(): ... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse()) ... A-7->B => arc inverse : False A-3->C => arc inverse : False B-5->C => arc inverse : False C-9->A => arc inverse : False >>> # On ajoute les arcs inverses >>> g.addArcsInversesNuls() >>> for arc in g.getArcs(): ... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse()) ... A-7->B => arc inverse : False A-3->C => arc inverse : False B-5->C => arc inverse : False C-9->A => arc inverse : False B-0->A => arc inverse : True C-0->A => arc inverse : True C-0->B => arc inverse : True A-0->C => arc inverse : True >>> random.seed(1) >>> # Ce graphe présente deux cycles : ABCA et ACA >>> # On modifie ABCA >>> g.kleinModifierCycleFromNoeud(A) >>> for arc in g.getArcs(): ... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse()) ... A-2->B => arc inverse : False A-3->C => arc inverse : False B-0->C => arc inverse : False C-4->A => arc inverse : False B-5->A => arc inverse : True C-0->A => arc inverse : True C-5->B => arc inverse : True A-5->C => arc inverse : True >>> # On supprime les arcs de poids nul >>> g.kleinDeleteDettesNulles() >>> for arc in g.getArcs(): ... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse()) ... A-2->B => arc inverse : False A-3->C => arc inverse : False C-4->A => arc inverse : False B-5->A => arc inverse : True C-5->B => arc inverse : True A-5->C => arc inverse : True >>> # On supprime les arcs inverses temporaires >>> g.kleinDeleteArcsInverses() >>> for arc in g.getArcs(): ... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse()) ... A-2->B => arc inverse : False A-3->C => arc inverse : False C-4->A => arc inverse : False
Expand source code
def kleinDeleteArcsInverses(self) -> None: """Supprime tous les arcs inverses temporaires. Tests ----- >>> g = Graphe('graphe') >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3) >>> BC5 = Arc('B-5->C',B,C,5) >>> CA8,CA1 = Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1) >>> g.setNoeuds([A,B,C]) >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) >>> # On simplifie le graphe >>> g.simplification() >>> for arc in g.getArcs(): ... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse()) ... A-7->B => arc inverse : False A-3->C => arc inverse : False B-5->C => arc inverse : False C-9->A => arc inverse : False >>> # On ajoute les arcs inverses >>> g.addArcsInversesNuls() >>> for arc in g.getArcs(): ... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse()) ... A-7->B => arc inverse : False A-3->C => arc inverse : False B-5->C => arc inverse : False C-9->A => arc inverse : False B-0->A => arc inverse : True C-0->A => arc inverse : True C-0->B => arc inverse : True A-0->C => arc inverse : True >>> random.seed(1) >>> # Ce graphe présente deux cycles : ABCA et ACA >>> # On modifie ABCA >>> g.kleinModifierCycleFromNoeud(A) >>> for arc in g.getArcs(): ... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse()) ... A-2->B => arc inverse : False A-3->C => arc inverse : False B-0->C => arc inverse : False C-4->A => arc inverse : False B-5->A => arc inverse : True C-0->A => arc inverse : True C-5->B => arc inverse : True A-5->C => arc inverse : True >>> # On supprime les arcs de poids nul >>> g.kleinDeleteDettesNulles() >>> for arc in g.getArcs(): ... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse()) ... A-2->B => arc inverse : False A-3->C => arc inverse : False C-4->A => arc inverse : False B-5->A => arc inverse : True C-5->B => arc inverse : True A-5->C => arc inverse : True >>> # On supprime les arcs inverses temporaires >>> g.kleinDeleteArcsInverses() >>> for arc in g.getArcs(): ... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse()) ... A-2->B => arc inverse : False A-3->C => arc inverse : False C-4->A => arc inverse : False """ # noqa : E501 pass # On récupère d'abord tous les arcs à supprimer # Car la suppression de proche en proche peut laisser # un arc à l'écart à cause de la modification du graphe arcsToDelete = [] for arc in self.getArcs(): if arc.getEstInverse(): arcsToDelete.append(arc) # On supprime alors tous les arcs à supprimer du graphe for arc in arcsToDelete: self.removeArc(arc)
def kleinDeleteDettesNulles(self) ‑> None
-
Supprime tous les arcs de poids nul.
Tests
>>> g = Graphe('graphe') >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3) >>> BC5 = Arc('B-5->C',B,C,5) >>> CA8,CA1 = Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1) >>> g.setNoeuds([A,B,C]) >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) >>> # On simplifie le graphe >>> g.simplification() >>> for arc in g.getArcs(): ... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse()) ... A-7->B => arc inverse : False A-3->C => arc inverse : False B-5->C => arc inverse : False C-9->A => arc inverse : False >>> # On ajoute les arcs inverses >>> g.addArcsInversesNuls() >>> random.seed(1) >>> # Ce graphe présente deux cycles : ABCA et ACA >>> # On modifie ABCA >>> g.kleinModifierCycleFromNoeud(A) >>> for arc in g.getArcs(): ... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse()) ... A-2->B => arc inverse : False A-3->C => arc inverse : False B-0->C => arc inverse : False C-4->A => arc inverse : False B-5->A => arc inverse : True C-0->A => arc inverse : True C-5->B => arc inverse : True A-5->C => arc inverse : True >>> # On supprime les arcs de poids nul >>> g.kleinDeleteDettesNulles() >>> for arc in g.getArcs(): ... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse()) ... A-2->B => arc inverse : False A-3->C => arc inverse : False C-4->A => arc inverse : False B-5->A => arc inverse : True C-5->B => arc inverse : True A-5->C => arc inverse : True
Expand source code
def kleinDeleteDettesNulles(self) -> None: """Supprime tous les arcs de poids nul. Tests ----- >>> g = Graphe('graphe') >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3) >>> BC5 = Arc('B-5->C',B,C,5) >>> CA8,CA1 = Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1) >>> g.setNoeuds([A,B,C]) >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) >>> # On simplifie le graphe >>> g.simplification() >>> for arc in g.getArcs(): ... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse()) ... A-7->B => arc inverse : False A-3->C => arc inverse : False B-5->C => arc inverse : False C-9->A => arc inverse : False >>> # On ajoute les arcs inverses >>> g.addArcsInversesNuls() >>> random.seed(1) >>> # Ce graphe présente deux cycles : ABCA et ACA >>> # On modifie ABCA >>> g.kleinModifierCycleFromNoeud(A) >>> for arc in g.getArcs(): ... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse()) ... A-2->B => arc inverse : False A-3->C => arc inverse : False B-0->C => arc inverse : False C-4->A => arc inverse : False B-5->A => arc inverse : True C-0->A => arc inverse : True C-5->B => arc inverse : True A-5->C => arc inverse : True >>> # On supprime les arcs de poids nul >>> g.kleinDeleteDettesNulles() >>> for arc in g.getArcs(): ... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse()) ... A-2->B => arc inverse : False A-3->C => arc inverse : False C-4->A => arc inverse : False B-5->A => arc inverse : True C-5->B => arc inverse : True A-5->C => arc inverse : True """ # noqa : E501 pass # On récupère d'abord tous les arcs à supprimer # Car la suppression de proche en proche peut laisser # un arc à l'écart à cause de la modification du graphe arcsToDelete = [] for arc in self.getArcs(): if arc.getPoids() == 0: arcsToDelete.append(arc) # On supprime alors tous les arcs à supprimer for arc in arcsToDelete: self.removeArc(arc)
def kleinEstCycle(self, arcsCycle: list) ‑> bool
-
Renvoie un booléen selon que arcsCycle est un cycle de Klein du graphe ou non.
Remarque
On n'utilise pas la méthode kleinAllArcsCycles() ici car elle exclut les doublons or tous les cycles doivent être testés par exemple ABCA et BCAB sont un doublon du même cycle mais doivent être testés tous les deux.
Paramètres
arcsCycle : list - Une liste d'arcs candidate à être un cycle de Klein.
Tests
>>> A, B, C = Noeud('A'), Noeud('B'), Noeud('C') >>> D, E, F = Noeud('D'), Noeud('E'), Noeud('F') >>> kleinAB, kleinBC = Arc('A-4->B', A, B, 4), Arc('B-6->C', B, C, 6) >>> kleinCD, kleinDE = Arc('C-5->D', C, D, 5), Arc('D-4->E', D, E, 4) >>> kleinEF, kleinFA = Arc('E-7->F', E, F, 7), Arc('F-5->A', F, A, 5) >>> kleinAE, kleinBE = Arc('A-2->E', A, E, 2), Arc('B-2->E', B, E, 2) >>> kleinDB, kleinFC = Arc('D-2->B', D, B, 2), Arc('F-3->C', F, C, 3) >>> G3 = Graphe('graphe3') >>> G3.setNoeuds([A, B, C, D, E, F]) >>> G3.setArcs([kleinAB, kleinBC, kleinCD, kleinDE, kleinEF]) >>> G3.addArcs([kleinFA, kleinAE, kleinBE, kleinDB, kleinFC]) >>> cycleOK = [kleinAB, kleinBC, kleinCD, kleinDE, kleinEF, kleinFA] >>> cycleKO = [kleinDB, kleinBE, Arc('E-1->D', E, D, 1)] >>> G3.kleinEstCycle(cycleOK) True >>> G3.kleinEstCycle(cycleKO) False
Expand source code
def kleinEstCycle(self, arcsCycle: list) -> bool: """Renvoie un booléen selon que arcsCycle est un cycle de Klein du graphe ou non. Remarque -------- On n'utilise pas la méthode kleinAllArcsCycles() ici car elle exclut les doublons or tous les cycles doivent être testés par exemple ABCA et BCAB sont un doublon du même cycle mais doivent être testés tous les deux. Paramètres ---------- arcsCycle : list - Une liste d'arcs candidate à être un cycle de Klein. Tests ----- >>> A, B, C = Noeud('A'), Noeud('B'), Noeud('C') >>> D, E, F = Noeud('D'), Noeud('E'), Noeud('F') >>> kleinAB, kleinBC = Arc('A-4->B', A, B, 4), Arc('B-6->C', B, C, 6) >>> kleinCD, kleinDE = Arc('C-5->D', C, D, 5), Arc('D-4->E', D, E, 4) >>> kleinEF, kleinFA = Arc('E-7->F', E, F, 7), Arc('F-5->A', F, A, 5) >>> kleinAE, kleinBE = Arc('A-2->E', A, E, 2), Arc('B-2->E', B, E, 2) >>> kleinDB, kleinFC = Arc('D-2->B', D, B, 2), Arc('F-3->C', F, C, 3) >>> G3 = Graphe('graphe3') >>> G3.setNoeuds([A, B, C, D, E, F]) >>> G3.setArcs([kleinAB, kleinBC, kleinCD, kleinDE, kleinEF]) >>> G3.addArcs([kleinFA, kleinAE, kleinBE, kleinDB, kleinFC]) >>> cycleOK = [kleinAB, kleinBC, kleinCD, kleinDE, kleinEF, kleinFA] >>> cycleKO = [kleinDB, kleinBE, Arc('E-1->D', E, D, 1)] >>> G3.kleinEstCycle(cycleOK) True >>> G3.kleinEstCycle(cycleKO) False """ pass for arc in arcsCycle: assert isinstance(arc, Arc), "Tous les éléments de arcsCycle doivent être des instances de la classe Arc." # noqa : E501 # booléen de sortie boolDeSortie = True # On récupère tous les cycles du graphe cyclesDuGraphe = [] for noeud in self.getNoeuds(): for cycle in self.kleinAllArcsCyclesFromNoeud(noeud): cyclesDuGraphe.append(cycle) if arcsCycle not in cyclesDuGraphe: boolDeSortie = False return boolDeSortie
def kleinHasCycle(self) ‑> None
-
Renvoie un booléen selon que le graphe a encore au moins un cycle de Klein.
Remarque
Un cycle de Klein est un cycle dont tous les arcs ont un poids non nul.
Tests
>>> g = Graphe('graphe') >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E') >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1) >>> BA = Arc('B->A',B,A,1) >>> CB = Arc('C->B',C,B,1) >>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1) >>> EA = Arc('E->A',E,A,1) >>> g.setNoeuds([A,B,C,D,E]) >>> g.setArcs([AB,AC,AD,BA,CB,DE,DB,EA]) >>> g.kleinHasCycle() True >>> AB.setPoids(0) >>> g.kleinHasCycle() True >>> BA.setPoids(0) >>> g.kleinHasCycle() True >>> DE.setPoids(0) >>> g.kleinHasCycle() False
Expand source code
def kleinHasCycle(self) -> None: """Renvoie un booléen selon que le graphe a encore au moins un cycle de Klein. Remarque -------- Un cycle de Klein est un cycle dont tous les arcs ont un poids non nul. Tests ----- >>> g = Graphe('graphe') >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E') >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1) >>> BA = Arc('B->A',B,A,1) >>> CB = Arc('C->B',C,B,1) >>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1) >>> EA = Arc('E->A',E,A,1) >>> g.setNoeuds([A,B,C,D,E]) >>> g.setArcs([AB,AC,AD,BA,CB,DE,DB,EA]) >>> g.kleinHasCycle() True >>> AB.setPoids(0) >>> g.kleinHasCycle() True >>> BA.setPoids(0) >>> g.kleinHasCycle() True >>> DE.setPoids(0) >>> g.kleinHasCycle() False """ # noqa : E501 pass boolDeSortie = False index = 0 noeudsDuGraphe = self.getNoeuds() while (not self.kleinHasCycleFromNoeud(noeudsDuGraphe[index]) and index < len(noeudsDuGraphe)-1): index += 1 if index < len(noeudsDuGraphe)-1: boolDeSortie = True return boolDeSortie
def kleinHasCycleFromNoeud(self, noeudCandidat: models.noeud.Noeud) ‑> bool
-
Renvoie un booléen selon que le graphe présente un cycle de Klein ou non à partir du noeudCandidat.
Paramètres
noeudCandidat : Noeud - Une instance de la classe Noeud.
Remarque
Un cycle de Klein est un cycle dont tous les arcs ont un poids non nul.
Tests
>>> g = Graphe('graphe') >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E') >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1) >>> BA = Arc('B->A',B,A,1) >>> CB = Arc('C->B',C,B,1) >>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1) >>> EA = Arc('E->A',E,A,1) >>> g.setNoeuds([A,B,C,D,E]) >>> g.setArcs([AB,AC,AD,BA,CB,DE,DB,EA]) >>> g.kleinHasCycleFromNoeud(A) True >>> AB.setPoids(0) >>> g.kleinHasCycleFromNoeud(A) True >>> BA.setPoids(0) >>> g.kleinHasCycleFromNoeud(A) True >>> DE.setPoids(0) >>> g.kleinHasCycleFromNoeud(A) False
Expand source code
def kleinHasCycleFromNoeud(self, noeudCandidat: Noeud) -> bool: """Renvoie un booléen selon que le graphe présente un cycle de Klein ou non à partir du noeudCandidat. Paramètres ---------- noeudCandidat : Noeud - Une instance de la classe Noeud. Remarque -------- Un cycle de Klein est un cycle dont tous les arcs ont un poids non nul. Tests ----- >>> g = Graphe('graphe') >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E') >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1) >>> BA = Arc('B->A',B,A,1) >>> CB = Arc('C->B',C,B,1) >>> DE,DB = Arc('D->E',D,E,1),Arc('D->B',D,B,1) >>> EA = Arc('E->A',E,A,1) >>> g.setNoeuds([A,B,C,D,E]) >>> g.setArcs([AB,AC,AD,BA,CB,DE,DB,EA]) >>> g.kleinHasCycleFromNoeud(A) True >>> AB.setPoids(0) >>> g.kleinHasCycleFromNoeud(A) True >>> BA.setPoids(0) >>> g.kleinHasCycleFromNoeud(A) True >>> DE.setPoids(0) >>> g.kleinHasCycleFromNoeud(A) False """ pass assert isinstance(noeudCandidat, Noeud), "Le noeudCandidat doit être un instance de la classe Noeud." # noqa : E501 # On récupère tous les noeuds situés à un arc de noeudCancidat noeudDirectementAccessibles = self.getCreanciers(noeudCandidat) boolDeSortie = False for noeud in noeudDirectementAccessibles: if self.kleinArcsBetween(noeudCandidat, noeud): if self.kleinAllArcsWays(noeud, noeudCandidat): boolDeSortie = True return boolDeSortie
def kleinModifierCycle(self, arcsCycle: list) ‑> None
-
Renvoie le graphe modifié après réduction du cycle.
Remarque
- Chaque arc dans le sens du cycle est modifié en diminuant son poids du plus petit poids des arcs du cycle.
- Chaque arc dans le sens inverse du cycle est aussi modifié en augmentant son poids du plus petit poids des arcs du cycle.
Paramètres
arcsCycle : list - Le cycle de Klein à réduire.
Tests
>>> A, B, C = Noeud('A'), Noeud('B'), Noeud('C') >>> D, E, F = Noeud('D'), Noeud('E'), Noeud('F') >>> kleinAB, kleinBC = Arc('A-4->B', A, B, 4), Arc('B-6->C', B, C, 6) >>> kleinCD, kleinDE = Arc('C-5->D', C, D, 5), Arc('D-4->E', D, E, 4) >>> kleinEF, kleinFA = Arc('E-7->F', E, F, 7), Arc('F-5->A', F, A, 5) >>> kleinAE, kleinBE = Arc('A-2->E', A, E, 2), Arc('B-2->E', B, E, 2) >>> kleinDB, kleinFC = Arc('D-2->B', D, B, 2), Arc('F-3->C', F, C, 3) >>> G3 = Graphe('graphe3') >>> G3.setNoeuds([A, B, C, D, E, F]) >>> G3.setArcs([kleinAB, kleinBC, kleinCD, kleinDE, kleinEF]) >>> G3.addArcs([kleinFA, kleinAE, kleinBE, kleinDB, kleinFC]) >>> G3.addArcsInversesNuls() >>> cycleOK = [kleinAB, kleinBC, kleinCD, kleinDE, kleinEF, kleinFA] >>> for arc in cycleOK: ... print(arc.getNom()) ... A-4->B B-6->C C-5->D D-4->E E-7->F F-5->A >>> G3.kleinModifierCycle(cycleOK) >>> for arc in cycleOK: ... print(arc.getNom()) ... A-0->B B-2->C C-1->D D-0->E E-3->F F-1->A
Expand source code
def kleinModifierCycle(self, arcsCycle: list) -> None: """Renvoie le graphe modifié après réduction du cycle. Remarque -------- - Chaque arc dans le sens du cycle est modifié en diminuant son poids du plus petit poids des arcs du cycle. - Chaque arc dans le sens inverse du cycle est aussi modifié en augmentant son poids du plus petit poids des arcs du cycle. Paramètres ---------- arcsCycle : list - Le cycle de Klein à réduire. Tests ----- >>> A, B, C = Noeud('A'), Noeud('B'), Noeud('C') >>> D, E, F = Noeud('D'), Noeud('E'), Noeud('F') >>> kleinAB, kleinBC = Arc('A-4->B', A, B, 4), Arc('B-6->C', B, C, 6) >>> kleinCD, kleinDE = Arc('C-5->D', C, D, 5), Arc('D-4->E', D, E, 4) >>> kleinEF, kleinFA = Arc('E-7->F', E, F, 7), Arc('F-5->A', F, A, 5) >>> kleinAE, kleinBE = Arc('A-2->E', A, E, 2), Arc('B-2->E', B, E, 2) >>> kleinDB, kleinFC = Arc('D-2->B', D, B, 2), Arc('F-3->C', F, C, 3) >>> G3 = Graphe('graphe3') >>> G3.setNoeuds([A, B, C, D, E, F]) >>> G3.setArcs([kleinAB, kleinBC, kleinCD, kleinDE, kleinEF]) >>> G3.addArcs([kleinFA, kleinAE, kleinBE, kleinDB, kleinFC]) >>> G3.addArcsInversesNuls() >>> cycleOK = [kleinAB, kleinBC, kleinCD, kleinDE, kleinEF, kleinFA] >>> for arc in cycleOK: ... print(arc.getNom()) ... A-4->B B-6->C C-5->D D-4->E E-7->F F-5->A >>> G3.kleinModifierCycle(cycleOK) >>> for arc in cycleOK: ... print(arc.getNom()) ... A-0->B B-2->C C-1->D D-0->E E-3->F F-1->A """ # noqa : E501 pass assert self.kleinEstCycle(arcsCycle), "arcsCycle doit être un cycle de Klein du Graphe." # noqa : E501 for arc in arcsCycle: assert isinstance(arc, Arc), "Tous les éléments de arcsCycle doivent être des instances de la classe Arc." # noqa : E501 minPoids = self.getMinPoidsFromCycle(arcsCycle) for arc in arcsCycle: # On diminue le poids de l'arc arc.setPoids(arc.getPoids()-minPoids) # On augmente le poids de son arc inverse arcInverse = self.getArcInverse(arc) arcInverse.setPoids(arcInverse.getPoids()+minPoids) # On renomme les arcs nouveauNom = arc.getNoeudDebut().getNom()+'-'+str(arc.getPoids())+'->'+arc.getNoeudFin().getNom() # noqa : E501 arc.setNom(nouveauNom) nouveauNomInverse = arcInverse.getNoeudDebut().getNom()+'-'+str(arcInverse.getPoids())+'->'+arcInverse.getNoeudFin().getNom() # noqa : E501 arcInverse.setNom(nouveauNomInverse)
def kleinModifierCycleFromNoeud(self, noeudCandidat: models.noeud.Noeud) ‑> None
-
Modifie un cycle de Klein pour un noeud du graphe s'il en existe, sinon le graphe n'est pas modifié.
- Chaque arc dans le sens du cycle est modifié en diminuant son poids du plus petit poids des arcs du cycle.
- Chaque arc dans le sens inverse du cycle est aussi modifié en augmentant son poids du plus petit poids des arcs du cycle.
Renvoie l'instance du graphe modifiée ou pas selon l'existence d'un cycle de Klein.
Paramètres
noeudCandidat : Noeud - Une instance de la classe Noeud.
Remarque
- Un cycle de Klein est un cycle :
- dont tous les arcs ont un poids non nul.
- dont le poids algébrique est strictement positif.
- Le poids algébrique est obtenu en ajoutant le poids des arcs directs et en enlevant le poids des arcs inverses.
Tests
>>> g = Graphe('graphe') >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3) >>> BC5 = Arc('B-5->C',B,C,5) >>> CA8,CA1 = Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1) >>> g.setNoeuds([A,B,C]) >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) >>> # On simplifie le graphe >>> g.simplification() >>> for arc in g.getArcs(): ... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse()) ... A-7->B => arc inverse : False A-3->C => arc inverse : False B-5->C => arc inverse : False C-9->A => arc inverse : False >>> # On ajoute les arcs inverses >>> g.addArcsInversesNuls() >>> for arc in g.getArcs(): ... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse()) ... A-7->B => arc inverse : False A-3->C => arc inverse : False B-5->C => arc inverse : False C-9->A => arc inverse : False B-0->A => arc inverse : True C-0->A => arc inverse : True C-0->B => arc inverse : True A-0->C => arc inverse : True >>> # Ce graphe présente deux cycles : ABCA et ACA >>> # On modifie ACA >>> random.seed(11) >>> g.kleinModifierCycleFromNoeud(A) >>> for arc in g.getArcs(): ... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse()) ... A-7->B => arc inverse : False A-0->C => arc inverse : False B-5->C => arc inverse : False C-6->A => arc inverse : False B-0->A => arc inverse : True C-3->A => arc inverse : True C-0->B => arc inverse : True A-3->C => arc inverse : True >>> # Il ne reste qu'un cycle ABCA, ACA est exclu car constitué par un arc et son propre inverse >>> for cycle in g.kleinAllArcsCyclesFromNoeud(A): ... print("=== cycle ===") ... for arc in cycle: ... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse()) ... === cycle === A-7->B => arc inverse : False B-5->C => arc inverse : False C-6->A => arc inverse : False === cycle === A-7->B => arc inverse : False B-5->C => arc inverse : False C-3->A => arc inverse : True
Expand source code
def kleinModifierCycleFromNoeud(self, noeudCandidat: Noeud) -> None: """Modifie un cycle de Klein pour un noeud du graphe s'il en existe, sinon le graphe n'est pas modifié. - Chaque arc dans le sens du cycle est modifié en diminuant son poids du plus petit poids des arcs du cycle. - Chaque arc dans le sens inverse du cycle est aussi modifié en augmentant son poids du plus petit poids des arcs du cycle. Renvoie l'instance du graphe modifiée ou pas selon l'existence d'un cycle de Klein. Paramètres ---------- noeudCandidat : Noeud - Une instance de la classe Noeud. Remarque -------- - Un cycle de Klein est un cycle : - dont tous les arcs ont un poids non nul. - dont le poids algébrique est strictement positif. - Le poids algébrique est obtenu en ajoutant le poids des arcs directs et en enlevant le poids des arcs inverses. Tests ----- >>> g = Graphe('graphe') >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> AB5,AB2,AC3 = Arc('A-5->B',A,B,5),Arc('A-2->B',A,B,2),Arc('A-3->C',A,C,3) >>> BC5 = Arc('B-5->C',B,C,5) >>> CA8,CA1 = Arc('C-8->A',C,A,8),Arc('C-1->A',C,A,1) >>> g.setNoeuds([A,B,C]) >>> g.setArcs([AB5,AB2,AC3,BC5,CA8,CA1]) >>> # On simplifie le graphe >>> g.simplification() >>> for arc in g.getArcs(): ... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse()) ... A-7->B => arc inverse : False A-3->C => arc inverse : False B-5->C => arc inverse : False C-9->A => arc inverse : False >>> # On ajoute les arcs inverses >>> g.addArcsInversesNuls() >>> for arc in g.getArcs(): ... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse()) ... A-7->B => arc inverse : False A-3->C => arc inverse : False B-5->C => arc inverse : False C-9->A => arc inverse : False B-0->A => arc inverse : True C-0->A => arc inverse : True C-0->B => arc inverse : True A-0->C => arc inverse : True >>> # Ce graphe présente deux cycles : ABCA et ACA >>> # On modifie ACA >>> random.seed(11) >>> g.kleinModifierCycleFromNoeud(A) >>> for arc in g.getArcs(): ... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse()) ... A-7->B => arc inverse : False A-0->C => arc inverse : False B-5->C => arc inverse : False C-6->A => arc inverse : False B-0->A => arc inverse : True C-3->A => arc inverse : True C-0->B => arc inverse : True A-3->C => arc inverse : True >>> # Il ne reste qu'un cycle ABCA, ACA est exclu car constitué par un arc et son propre inverse >>> for cycle in g.kleinAllArcsCyclesFromNoeud(A): ... print("=== cycle ===") ... for arc in cycle: ... print(arc.getNom(), "=> arc inverse : ", arc.getEstInverse()) ... === cycle === A-7->B => arc inverse : False B-5->C => arc inverse : False C-6->A => arc inverse : False === cycle === A-7->B => arc inverse : False B-5->C => arc inverse : False C-3->A => arc inverse : True """ # noqa : E501 pass assert isinstance(noeudCandidat, Noeud), "Le noeudCandidat doit être un instance de la classe Noeud." # noqa : E501 # Pour chaque arc du cycle, il doit y avoir un arc inverse for arc in self.getArcs(): assert self.hasArcInverse(arc), "Tous les arcs du graphe doivent avoir un arc inverse" # noqa : E501 if self.kleinHasCycleFromNoeud(noeudCandidat): # On choisit un cycle à réduire au hasard cycleToChange = random.choice( self.kleinAllArcsCyclesFromNoeud(noeudCandidat) ) self.kleinModifierCycle(cycleToChange)
def parcoursEnProfondeurArcs(self, noeudDepart: models.noeud.Noeud, dejaVisites=None) ‑> list
-
Renvoie les arcs du graphe accessibles depuis noeudDepart.
Paramètres
- noeudDepart : Noeud - Le noeud dont on veut les arcs accessibles.
- dejaVisites : list - Les arcs déjà visités.
Remarque
- Fonction recursive
Tests
>>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E') >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1) >>> BA = Arc('B->A',B,A,1) >>> CB = Arc('C->B',C,B,1) >>> DE = Arc('D->E',D,E,2) >>> EA = Arc('E->A',E,A,3) >>> g = Graphe('graphe') >>> g.setNoeuds([A,B,C,D,E]) >>> g.setArcs([AB,AC,AD,CB,DE,EA]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'A->D', 'C->B', 'D->E', 'E->A']) >>> for n in g.parcoursEnProfondeurArcs(A,[]): ... print(n.getNom()) ... A->B C->B D->E E->A A->C A->D >>> g1 = Graphe('graphe1') >>> g1.setNoeuds([A,B,C,D,E]) >>> g1.setArcs([AB,AC,BA,CB,DE,EA]) >>> print(g1) Graphe(nom=graphe1, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'B->A', 'C->B', 'D->E', 'E->A']) >>> for n in g1.parcoursEnProfondeurArcs(A,[]): ... print(n.getNom()) ... A->B B->A A->C C->B
Expand source code
def parcoursEnProfondeurArcs( self, noeudDepart: Noeud, dejaVisites=None ) -> list: """Renvoie les arcs du graphe accessibles depuis noeudDepart. Paramètres ---------- - noeudDepart : Noeud - Le noeud dont on veut les arcs accessibles. - dejaVisites : list - Les arcs déjà visités. Remarque -------- - Fonction recursive Tests ----- >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E') >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1) >>> BA = Arc('B->A',B,A,1) >>> CB = Arc('C->B',C,B,1) >>> DE = Arc('D->E',D,E,2) >>> EA = Arc('E->A',E,A,3) >>> g = Graphe('graphe') >>> g.setNoeuds([A,B,C,D,E]) >>> g.setArcs([AB,AC,AD,CB,DE,EA]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'A->D', 'C->B', 'D->E', 'E->A']) >>> for n in g.parcoursEnProfondeurArcs(A,[]): ... print(n.getNom()) ... A->B C->B D->E E->A A->C A->D >>> g1 = Graphe('graphe1') >>> g1.setNoeuds([A,B,C,D,E]) >>> g1.setArcs([AB,AC,BA,CB,DE,EA]) >>> print(g1) Graphe(nom=graphe1, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'B->A', 'C->B', 'D->E', 'E->A']) >>> for n in g1.parcoursEnProfondeurArcs(A,[]): ... print(n.getNom()) ... A->B B->A A->C C->B """ # noqa : E501 pass assert isinstance(noeudDepart, Noeud), "arcDepart doit être un instance de la classe Arc." # noqa : E501 assert isinstance(dejaVisites,list) or dejaVisites is None,"dejaVisites vaut None ou est une liste." # noqa : E501 if dejaVisites is None: dejaVisites = [] for arc in self.getArcsCreanciers(noeudDepart): if arc not in dejaVisites: dejaVisites.append(arc) for voisin in self.getCreanciers(noeudDepart): self.parcoursEnProfondeurArcs(voisin, dejaVisites) return dejaVisites
def parcoursEnProfondeurNoeuds(self, noeudDepart: models.noeud.Noeud, dejaVisites=None) ‑> list
-
Renvoie les noeuds du graphe accessibles depuis noeudDepart.
Paramètres
- noeudDepart : Noeud - Le noeud de départ de l'exploration.
- dejaVisites : list - Une liste avec les noeuds déjà visités.
Remarque
- Fonction recursive
Tests
>>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E') >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1) >>> BA = Arc('B->A',B,A,1) >>> CB = Arc('C->B',C,B,1) >>> DE = Arc('D->E',D,E,2) >>> EA = Arc('E->A',E,A,3) >>> g = Graphe('graphe') >>> g.setNoeuds([A,B,C,D,E]) >>> g.setArcs([AB,AC,AD,CB,DE,EA]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'A->D', 'C->B', 'D->E', 'E->A']) >>> for n in g.parcoursEnProfondeurNoeuds(A,[]): ... print(n.getNom()) ... A B C D E >>> g1 = Graphe('graphe1') >>> g1.setNoeuds([A,B,C,D,E]) >>> g1.setArcs([AB,AC,BA,CB,DE,EA]) >>> print(g1) Graphe(nom=graphe1, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'B->A', 'C->B', 'D->E', 'E->A']) >>> for n in g1.parcoursEnProfondeurNoeuds(A,[]): ... print(n.getNom()) ... A B C
Expand source code
def parcoursEnProfondeurNoeuds( self, noeudDepart: Noeud, dejaVisites=None ) -> list: """Renvoie les noeuds du graphe accessibles depuis noeudDepart. Paramètres ---------- - noeudDepart : Noeud - Le noeud de départ de l'exploration. - dejaVisites : list - Une liste avec les noeuds déjà visités. Remarque -------- - Fonction recursive Tests ----- >>> A,B,C,D,E = Noeud('A'),Noeud('B'),Noeud('C'),Noeud('D'),Noeud('E') >>> AB,AC,AD = Arc('A->B',A,B,1),Arc('A->C',A,C,1),Arc('A->D',A,D,1) >>> BA = Arc('B->A',B,A,1) >>> CB = Arc('C->B',C,B,1) >>> DE = Arc('D->E',D,E,2) >>> EA = Arc('E->A',E,A,3) >>> g = Graphe('graphe') >>> g.setNoeuds([A,B,C,D,E]) >>> g.setArcs([AB,AC,AD,CB,DE,EA]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'A->D', 'C->B', 'D->E', 'E->A']) >>> for n in g.parcoursEnProfondeurNoeuds(A,[]): ... print(n.getNom()) ... A B C D E >>> g1 = Graphe('graphe1') >>> g1.setNoeuds([A,B,C,D,E]) >>> g1.setArcs([AB,AC,BA,CB,DE,EA]) >>> print(g1) Graphe(nom=graphe1, noeuds=['A', 'B', 'C', 'D', 'E'], arcs=['A->B', 'A->C', 'B->A', 'C->B', 'D->E', 'E->A']) >>> for n in g1.parcoursEnProfondeurNoeuds(A,[]): ... print(n.getNom()) ... A B C """ # noqa : E501 pass assert isinstance(noeudDepart, Noeud), "Le noeudDepart doit être un instance de la classe Noeud." # noqa : E501 assert isinstance(dejaVisites,list) or dejaVisites is None,"dejaVisites vaut None ou est une liste." # noqa : E501 if dejaVisites is None: dejaVisites = [] if noeudDepart not in dejaVisites: dejaVisites.append(noeudDepart) for voisin in self.getCreanciers(noeudDepart): self.parcoursEnProfondeurNoeuds(voisin, dejaVisites) return dejaVisites
def positionNette(self, noeudCandidat: models.noeud.Noeud, algo1NomEntreprise=None, algo1Don=0) ‑> int
-
Retourne la balance entre dettes et créances d'une instance de la classe Noeud.
Paramètres
- noeudCandidat : Noeud - Une instance de la classe Noeud dont on veut les créanciers.
- algo1NomEntreprise : str - Le nom de l'entreprise choisie pour le don dans l'algorithme 1 - méthode heuristique
- algo1Don : int - Une valeur correspondant au don dans l'algorithme 1 méthode heuristique
Tests
>>> g1 = Graphe('graphe1') >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> # arcs partant de A >>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3) >>> # arcs arrivant sur A >>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1) >>> # arc entre B et C >>> BC = Arc('BC',B,C,5) >>> g1.setNoeuds([A,B,C]) >>> g1.setArcs([AB1,AB2,AC,CA1,CA2,BC]) >>> g1.positionNette(A) -1 >>> g1.positionNette(A,'A',5) -6 >>> g1.positionNette(B) 2 >>> g1.positionNette(B,'A',5) 2 >>> g1.positionNette(C) -1
Expand source code
def positionNette( self, noeudCandidat: Noeud, algo1NomEntreprise=None, algo1Don=0 ) -> int: """Retourne la balance entre dettes et créances d'une instance de la classe Noeud. Paramètres ---------- - noeudCandidat : Noeud - Une instance de la classe Noeud dont on veut les créanciers. - algo1NomEntreprise : str - Le nom de l'entreprise choisie pour le don dans l'algorithme 1 - méthode heuristique - algo1Don : int - Une valeur correspondant au don dans l'algorithme 1 méthode heuristique Tests ----- >>> g1 = Graphe('graphe1') >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> # arcs partant de A >>> AB1,AB2,AC = Arc('AB1',A,B,5),Arc('AB2',A,B,2),Arc('AC',A,C,3) >>> # arcs arrivant sur A >>> CA1,CA2 = Arc('CA1',C,A,8),Arc('CA2',C,A,1) >>> # arc entre B et C >>> BC = Arc('BC',B,C,5) >>> g1.setNoeuds([A,B,C]) >>> g1.setArcs([AB1,AB2,AC,CA1,CA2,BC]) >>> g1.positionNette(A) -1 >>> g1.positionNette(A,'A',5) -6 >>> g1.positionNette(B) 2 >>> g1.positionNette(B,'A',5) 2 >>> g1.positionNette(C) -1 """ # noqa : E501 pass assert self.isNoeudInGraphe(noeudCandidat), "noeudCandidat doit être un noeud du graphe." # noqa : E501 # Ne pas utiliser les assertions pour valider les données # car elles peuvent être désactivées. # On lève donc une erreur en cas de souci if not self.isNoeudInGraphe(noeudCandidat): raise ValueError("noeudCandidat doit être un noeud du graphe.") else: # Total des sommes dues aux créanciers du noeudCandidat detteTotaleDuNoeud = 0 # Total des sommes dues par les débiteurs du noeudCandidat creanceTotaleDuNoeud = 0 debiteurs = self.getDebiteurs(noeudCandidat) creanciers = self.getCreanciers(noeudCandidat) for arc in self.getArcs(): noeudDebut = arc.getNoeudDebut() noeudFin = arc.getNoeudFin() if noeudDebut == noeudCandidat and noeudFin in creanciers: detteTotaleDuNoeud += arc.poids if noeudDebut in debiteurs and noeudFin == noeudCandidat: creanceTotaleDuNoeud += arc.poids if noeudCandidat.getNom() == algo1NomEntreprise: positionNetteDuNoeud = (noeudCandidat.getCapital() + creanceTotaleDuNoeud - detteTotaleDuNoeud - algo1Don) else: positionNetteDuNoeud = (noeudCandidat.getCapital() + creanceTotaleDuNoeud - detteTotaleDuNoeud) return positionNetteDuNoeud
def removeArc(self, arcCandidat: models.arc.Arc) ‑> None
-
Supprime une instance de la classe Arc du graphe.
Paramètres
arcCandidat : Arc - Un arc à supprimer du graphe.
Tests
>>> g1 = Graphe('graphe1') >>> print(g1) Graphe(nom=graphe1, noeuds=[], arcs=[]) >>> n1,n2,n3 = Noeud('n1'),Noeud('n2'),Noeud('n3') >>> g1.setNoeuds([n1,n2,n3]) >>> print(g1) Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=[]) >>> a1 = Arc('n1n2_1', n1, n2, 1) >>> a2 = Arc('n1n2_2', n1, n2, 2) >>> a3 = Arc('n1n3', n1, n3, 3) >>> g1.setArcs([a1,a2,a3]) >>> print(g1) Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=['n1n2_1', 'n1n2_2', 'n1n3']) >>> g1.removeArc(a1) >>> print(g1) Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=['n1n2_2', 'n1n3']) >>> for n in g1.getNoeuds(): ... print(n) ... Noeud(nom=n1, voisins=['n2', 'n3'], nbArcs=2, capital=0) Noeud(nom=n2, voisins=['n1'], nbArcs=1, capital=0) Noeud(nom=n3, voisins=['n1'], nbArcs=1, capital=0) >>> g1.removeArc(a3) >>> for n in g1.getNoeuds(): ... print(n) ... Noeud(nom=n1, voisins=['n2'], nbArcs=1, capital=0) Noeud(nom=n2, voisins=['n1'], nbArcs=1, capital=0) Noeud(nom=n3, voisins=[], nbArcs=0, capital=0)
Expand source code
def removeArc(self, arcCandidat: Arc) -> None: """Supprime une instance de la classe Arc du graphe. Paramètres ---------- arcCandidat : Arc - Un arc à supprimer du graphe. Tests ----- >>> g1 = Graphe('graphe1') >>> print(g1) Graphe(nom=graphe1, noeuds=[], arcs=[]) >>> n1,n2,n3 = Noeud('n1'),Noeud('n2'),Noeud('n3') >>> g1.setNoeuds([n1,n2,n3]) >>> print(g1) Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=[]) >>> a1 = Arc('n1n2_1', n1, n2, 1) >>> a2 = Arc('n1n2_2', n1, n2, 2) >>> a3 = Arc('n1n3', n1, n3, 3) >>> g1.setArcs([a1,a2,a3]) >>> print(g1) Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=['n1n2_1', 'n1n2_2', 'n1n3']) >>> g1.removeArc(a1) >>> print(g1) Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=['n1n2_2', 'n1n3']) >>> for n in g1.getNoeuds(): ... print(n) ... Noeud(nom=n1, voisins=['n2', 'n3'], nbArcs=2, capital=0) Noeud(nom=n2, voisins=['n1'], nbArcs=1, capital=0) Noeud(nom=n3, voisins=['n1'], nbArcs=1, capital=0) >>> g1.removeArc(a3) >>> for n in g1.getNoeuds(): ... print(n) ... Noeud(nom=n1, voisins=['n2'], nbArcs=1, capital=0) Noeud(nom=n2, voisins=['n1'], nbArcs=1, capital=0) Noeud(nom=n3, voisins=[], nbArcs=0, capital=0) """ # noqa : E501 pass assert isinstance(arcCandidat, Arc), "arc doit être une instance de la classe Arc." # noqa : E501 assert self.isArcInGraphe(arcCandidat), "L'arc à supprimer doit faire partie des arcs du graphe." # noqa : E501 # Un booléen pour tester si tout est OK allOk = True # Ne pas utiliser les assertions pour valider les données # car elles peuvent être désactivées. # On lève donc une erreur en cas de souci if not self.isArcInGraphe(arcCandidat): allOk = False raise ValueError("L'arc à supprimer doit faire partie des arcs du graphe.") # noqa : E501 if allOk: self.getArcs().remove(arcCandidat) # Après suppression de l'arcCandidat, # Si il n'y a plus d'arcs entre eux # On met à jour le tableau des voisins des Noeuds # Sinon, on gère uniquement les nombres d'arcs des Noeuds ! noeudDebut = arcCandidat.getNoeudDebut() noeudFin = arcCandidat.getNoeudFin() if ((noeudDebut not in self.getDebiteurs(noeudFin)) and (noeudDebut not in self.getCreanciers(noeudFin))): noeudDebut.removeVoisin(noeudFin) else: noeudDebut.setNbArcs(noeudDebut.getNbArcs() - 1) noeudFin.setNbArcs(noeudFin.getNbArcs() - 1)
def setArcs(self, nouveauxArcs: list) ‑> None
-
Définit/Ajoute au tableau des arcs d'une instance de la classe Graphe un ensemble de nouveaux arcs.
Paramètres
nouveauxArcs : list - Une liste d'instances de la classe Arcs.
Tests
>>> g1 = Graphe('graphe1') >>> print(g1) Graphe(nom=graphe1, noeuds=[], arcs=[]) >>> n1 = Noeud('n1') >>> n2 = Noeud('n2') >>> n3 = Noeud('n3') >>> nouveauxNoeuds = [n1, n2, n3] >>> g1.setNoeuds(nouveauxNoeuds) >>> print(g1) Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=[]) >>> a1 = Arc('n1n2_0', n1, n2, 1) >>> a2 = Arc('n1n2_1', n1, n2, 2) >>> a3 = Arc('n1n3_0', n1, n3, 3) >>> a4 = Arc('n2n3_0', n2, n3, 4) >>> nouveauxArcs = [a1, a2, a3, a4] >>> g1.setArcs(nouveauxArcs) >>> print(g1) Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=['n1n2_0', 'n1n2_1', 'n1n3_0', 'n2n3_0'])
Expand source code
def setArcs(self, nouveauxArcs: list) -> None: """Définit/Ajoute au tableau des arcs d'une instance de la classe Graphe un ensemble de nouveaux arcs. Paramètres ---------- nouveauxArcs : list - Une liste d'instances de la classe Arcs. Tests ----- >>> g1 = Graphe('graphe1') >>> print(g1) Graphe(nom=graphe1, noeuds=[], arcs=[]) >>> n1 = Noeud('n1') >>> n2 = Noeud('n2') >>> n3 = Noeud('n3') >>> nouveauxNoeuds = [n1, n2, n3] >>> g1.setNoeuds(nouveauxNoeuds) >>> print(g1) Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=[]) >>> a1 = Arc('n1n2_0', n1, n2, 1) >>> a2 = Arc('n1n2_1', n1, n2, 2) >>> a3 = Arc('n1n3_0', n1, n3, 3) >>> a4 = Arc('n2n3_0', n2, n3, 4) >>> nouveauxArcs = [a1, a2, a3, a4] >>> g1.setArcs(nouveauxArcs) >>> print(g1) Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=['n1n2_0', 'n1n2_1', 'n1n3_0', 'n2n3_0']) """ # noqa : E501 pass # Un booléen pour tester si tout est OK allOk = True for nouvelArc in nouveauxArcs: assert nouvelArc not in self.arcs, "On ne peut ajouter un arc déjà présent." # noqa : E501 if nouvelArc in self.getArcs(): allOk = False raise ValueError("On ne peut ajouter un arc déjà présent.") assert nouvelArc.noeudDebut in self.noeuds or nouvelArc.noeudFin in self.noeuds, "NouvelArc doit lier deux noeuds du graphe." # noqa : E501 if (nouvelArc.getNoeudDebut() not in self.getNoeuds() or nouvelArc.getNoeudFin() not in self.getNoeuds()): allOk = False raise ValueError("NouvelArc doit lier deux noeuds du graphe.") if allOk: for arc in nouveauxArcs: self.addArc(arc)
def setNoeuds(self, nouveauxNoeuds: list) ‑> None
-
Définit le tableau des noeuds d'une instance de la classe Graphe.
Paramètres
nouveauxNoeuds : list - Une liste d'instances de la classe Noeuds.
Remarque:
On ne définit les noeuds d'un graphe que si le graphe n'a aucun noeud.
Tests
>>> g1 = Graphe('graphe1') >>> print(g1) Graphe(nom=graphe1, noeuds=[], arcs=[]) >>> n1 = Noeud('n1') >>> n2 = Noeud('n2') >>> n3 = Noeud('n3') >>> nouveauxNoeuds = [n1, n2, n3] >>> g1.setNoeuds(nouveauxNoeuds) >>> print(g1) Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=[])
Expand source code
def setNoeuds(self, nouveauxNoeuds: list) -> None: """Définit le tableau des noeuds d'une instance de la classe Graphe. Paramètres ---------- nouveauxNoeuds : list - Une liste d'instances de la classe Noeuds. Remarque: --------- On ne définit les noeuds d'un graphe que si le graphe n'a aucun noeud. Tests ----- >>> g1 = Graphe('graphe1') >>> print(g1) Graphe(nom=graphe1, noeuds=[], arcs=[]) >>> n1 = Noeud('n1') >>> n2 = Noeud('n2') >>> n3 = Noeud('n3') >>> nouveauxNoeuds = [n1, n2, n3] >>> g1.setNoeuds(nouveauxNoeuds) >>> print(g1) Graphe(nom=graphe1, noeuds=['n1', 'n2', 'n3'], arcs=[]) """ pass # Un booléen pour tester si tout est OK allOk = True for nouveauNoeud in nouveauxNoeuds: assert nouveauNoeud not in self.noeuds, "On ne peut ajouter un noeud déjà présent." # noqa : E501 if nouveauNoeud in self.noeuds: allOk = False raise ValueError("On ne peut ajouter un noeud déjà présent.") if allOk: for noeud in nouveauxNoeuds: self.addNoeud(noeud)
def setNom(self, nouveauNom) ‑> str
-
Redéfinit le nom du graphe.
Paramètres
nouveauNom : str - Le nouveau nom du graphe
Tests
>>> g1 = Graphe('graphe1') >>> g1.getNom() 'graphe1' >>> g1.setNom('new_graphe1') >>> g1.getNom() 'new_graphe1'
Expand source code
def setNom(self, nouveauNom) -> str: """Redéfinit le nom du graphe. Paramètres ---------- nouveauNom : str - Le nouveau nom du graphe Tests ----- >>> g1 = Graphe('graphe1') >>> g1.getNom() 'graphe1' >>> g1.setNom('new_graphe1') >>> g1.getNom() 'new_graphe1' """ pass assert isinstance(nouveauNom, str), "Le nom d'un graphe doit être un string." # noqa : E501 self.nom = nouveauNom
def simplification(self) ‑> None
-
Modifie l'instance du graphe en remplaçant pour chaque couple d'entreprises A,B, l'ensemble des dettes de A vers B par une seule dette égale à la somme des dettes.
Tests
>>> g = Graphe('graphe') >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> AB2 = Arc('AB2',A,B,2) >>> BC7,BC4 = Arc('BC7',B,C,7),Arc('BC4',B,C,4) >>> CA2,CA5,CA9 = Arc('CA2',C,A,2),Arc('CA5',C,A,5),Arc('CA9',C,A,9) >>> g.setNoeuds([A,B,C]) >>> g.setArcs([AB2,BC7,BC4,CA2,CA5,CA9]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB2', 'BC7', 'BC4', 'CA2', 'CA5', 'CA9']) >>> g.simplification() >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-2->B', 'B-11->C', 'C-16->A']) >>> # Test sur le graphe du document de cours >>> g1 = Graphe('graphe1') >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> # arcs partant de A >>> AB,ABbis,AC = Arc('AB',A,B,5),Arc('ABbis',A,B,2),Arc('AC',A,C,3) >>> # arcs arrivant sur A >>> CA,CAbis = Arc('CA',C,A,8),Arc('CAbis',C,A,1) >>> # arc entre B et C >>> BC = Arc('BC',B,C,5) >>> g1.setNoeuds([A,B,C]) >>> g1.setArcs([AB,ABbis,AC,CA,CAbis,BC]) >>> print(g1) Graphe(nom=graphe1, noeuds=['A', 'B', 'C'], arcs=['AB', 'ABbis', 'AC', 'CA', 'CAbis', 'BC']) >>> for noeud in g1.getNoeuds(): ... print(noeud) ... Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0) Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=0) Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0) >>> g1.simplification() >>> print(g1) Graphe(nom=graphe1, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A']) >>> for noeud in g1.getNoeuds(): ... print(noeud) ... Noeud(nom=A, voisins=['B', 'C'], nbArcs=3, capital=0) Noeud(nom=B, voisins=['A', 'C'], nbArcs=2, capital=0) Noeud(nom=C, voisins=['A', 'B'], nbArcs=3, capital=0)
Expand source code
def simplification(self) -> None: """Modifie l'instance du graphe en remplaçant pour chaque couple d'entreprises A,B, l'ensemble des dettes de A vers B par une seule dette égale à la somme des dettes. Tests ----- >>> g = Graphe('graphe') >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> AB2 = Arc('AB2',A,B,2) >>> BC7,BC4 = Arc('BC7',B,C,7),Arc('BC4',B,C,4) >>> CA2,CA5,CA9 = Arc('CA2',C,A,2),Arc('CA5',C,A,5),Arc('CA9',C,A,9) >>> g.setNoeuds([A,B,C]) >>> g.setArcs([AB2,BC7,BC4,CA2,CA5,CA9]) >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['AB2', 'BC7', 'BC4', 'CA2', 'CA5', 'CA9']) >>> g.simplification() >>> print(g) Graphe(nom=graphe, noeuds=['A', 'B', 'C'], arcs=['A-2->B', 'B-11->C', 'C-16->A']) >>> # Test sur le graphe du document de cours >>> g1 = Graphe('graphe1') >>> A,B,C = Noeud('A'),Noeud('B'),Noeud('C') >>> # arcs partant de A >>> AB,ABbis,AC = Arc('AB',A,B,5),Arc('ABbis',A,B,2),Arc('AC',A,C,3) >>> # arcs arrivant sur A >>> CA,CAbis = Arc('CA',C,A,8),Arc('CAbis',C,A,1) >>> # arc entre B et C >>> BC = Arc('BC',B,C,5) >>> g1.setNoeuds([A,B,C]) >>> g1.setArcs([AB,ABbis,AC,CA,CAbis,BC]) >>> print(g1) Graphe(nom=graphe1, noeuds=['A', 'B', 'C'], arcs=['AB', 'ABbis', 'AC', 'CA', 'CAbis', 'BC']) >>> for noeud in g1.getNoeuds(): ... print(noeud) ... Noeud(nom=A, voisins=['B', 'C'], nbArcs=5, capital=0) Noeud(nom=B, voisins=['A', 'C'], nbArcs=3, capital=0) Noeud(nom=C, voisins=['A', 'B'], nbArcs=4, capital=0) >>> g1.simplification() >>> print(g1) Graphe(nom=graphe1, noeuds=['A', 'B', 'C'], arcs=['A-7->B', 'A-3->C', 'B-5->C', 'C-9->A']) >>> for noeud in g1.getNoeuds(): ... print(noeud) ... Noeud(nom=A, voisins=['B', 'C'], nbArcs=3, capital=0) Noeud(nom=B, voisins=['A', 'C'], nbArcs=2, capital=0) Noeud(nom=C, voisins=['A', 'B'], nbArcs=3, capital=0) """ # noqa : E501 pass # Si A et B sont voisins alors # soit A a des dettes envers B, soit B a des dettes envers A. newArcs = [] for noeud in self.getNoeuds(): for voisin in noeud.getVoisins(): detteTotale = 0 for arc in self.getArcs(): if (noeud == arc.getNoeudDebut() and voisin == arc.getNoeudFin()): # On cumule la dette detteTotale += arc.getPoids() if detteTotale != 0: # On ajoute un nouvel arc avec la dette totale # au tableau des nouveaux arcs nouveauNom = noeud.getNom()+'-'+str(detteTotale)+'->'+voisin.getNom() # noqa : E501 newArc = Arc(nouveauNom, noeud, voisin, detteTotale) newArcs.append(newArc) # Le graphe garde les mêmes noeuds # Mais on remplace tous les arcs # On commence par supprimer tous les arcs existants index = len(self.getArcs())-1 while index != -1: self.removeArc(self.getArcs()[index]) index -= 1 # On ajoute les nouveaux arcs self.setArcs(newArcs) # Il faut aussi redefinir tous les voisins for arc in newArcs: if not arc.getNoeudDebut().isVoisin(arc.getNoeudFin()): arc.getNoeudDebut().addVoisin(arc.getNoeudFin()) # Il faut aussi redefinir le nombre d'arcs de chaque noeud # On commence par mettre le nombre d'arcs à 0 for noeud in self.getNoeuds(): noeud.setNbArcs(0) # On met à jour le nombre d'arcs de chaque noeud for arc in newArcs: if noeud == arc.getNoeudDebut(): noeud.setNbArcs(noeud.getNbArcs()+1) if noeud == arc.getNoeudFin(): noeud.setNbArcs(noeud.getNbArcs()+1)
-