|
|
@ -1,10 +1,15 @@ |
|
|
import random |
|
|
import random |
|
|
import triangulation |
|
|
import triangulation |
|
|
|
|
|
import tas |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Graphe: |
|
|
class Graphe: |
|
|
"""Implémente un graphe non orienté.""" |
|
|
"""Implémente un graphe non orienté.""" |
|
|
|
|
|
|
|
|
|
|
|
class Cout: |
|
|
|
|
|
CARBURANT = 0 |
|
|
|
|
|
TEMPS = 1 |
|
|
|
|
|
|
|
|
def __init__(self, nom): |
|
|
def __init__(self, nom): |
|
|
"""Initialise un graphe vide. |
|
|
"""Initialise un graphe vide. |
|
|
|
|
|
|
|
|
@ -13,6 +18,7 @@ class Graphe: |
|
|
self.nom = nom |
|
|
self.nom = nom |
|
|
self.sommets = [] |
|
|
self.sommets = [] |
|
|
self.aretes = [] |
|
|
self.aretes = [] |
|
|
|
|
|
self.cout = Graphe.Cout.TEMPS |
|
|
|
|
|
|
|
|
def renomme(self, nom): |
|
|
def renomme(self, nom): |
|
|
"""Renome le graphe |
|
|
"""Renome le graphe |
|
|
@ -43,7 +49,7 @@ class Graphe: |
|
|
|
|
|
|
|
|
:return: L'arête créée. |
|
|
:return: L'arête créée. |
|
|
""" |
|
|
""" |
|
|
a = Arete(s1, s2, longueur, v_moyenne) |
|
|
a = Arete(s1, s2, longueur, v_moyenne, self) |
|
|
self.aretes.append(a) |
|
|
self.aretes.append(a) |
|
|
return a |
|
|
return a |
|
|
|
|
|
|
|
|
@ -67,10 +73,11 @@ class Graphe: |
|
|
|
|
|
|
|
|
:param dest: instance de Afficheur. |
|
|
:param dest: instance de Afficheur. |
|
|
""" |
|
|
""" |
|
|
|
|
|
dest.renomme(self.nom) |
|
|
for s in self.sommets: |
|
|
for s in self.sommets: |
|
|
dest.changeCouleur(s.couleur) |
|
|
dest.changeCouleur(s.couleur) |
|
|
dest.tracePoint((s.x(), s.y())) |
|
|
dest.tracePoint((s.x(), s.y())) |
|
|
dest.traceTexte((s.x(), s.y()), 'v'+str(s.num)) |
|
|
dest.traceTexte((s.x(), s.y()), 'v' + str(s.num)) |
|
|
|
|
|
|
|
|
for a in self.aretes: |
|
|
for a in self.aretes: |
|
|
dest.changeCouleur(a.couleur) |
|
|
dest.changeCouleur(a.couleur) |
|
|
@ -111,6 +118,123 @@ class Graphe: |
|
|
a.couleur = (1., 1., 0.) |
|
|
a.couleur = (1., 1., 0.) |
|
|
return a |
|
|
return a |
|
|
|
|
|
|
|
|
|
|
|
def dijkstra(self, depart=None): |
|
|
|
|
|
"""Calcule les plus courts chemins depuis le sommet `depart` (attention, |
|
|
|
|
|
effets de bord). |
|
|
|
|
|
|
|
|
|
|
|
:param depart: Sommet de départ. |
|
|
|
|
|
""" |
|
|
|
|
|
for s in self.sommets: |
|
|
|
|
|
s.cumul = None |
|
|
|
|
|
s.precedent = None |
|
|
|
|
|
sommets = [depart or self.sommets[0]] |
|
|
|
|
|
sommets[0].cumul = 0 |
|
|
|
|
|
while sommets: |
|
|
|
|
|
i, s = min(enumerate(sommets), key=lambda x: x[1].cumul) |
|
|
|
|
|
sommets.pop(i) |
|
|
|
|
|
for arete in s.aretes: |
|
|
|
|
|
voisin = arete.voisin(s) |
|
|
|
|
|
if voisin.cumul is None: # cumul infini |
|
|
|
|
|
sommets.append(voisin) |
|
|
|
|
|
if voisin.cumul is None or s.cumul + arete.cout < voisin.cumul: |
|
|
|
|
|
voisin.cumul = s.cumul + arete.cout |
|
|
|
|
|
voisin.precedent = arete |
|
|
|
|
|
|
|
|
|
|
|
def dijkstraAvecTas(self, depart=None): |
|
|
|
|
|
"""Calcule les plus courts chemins depuis le sommet `depart` (attention, |
|
|
|
|
|
effets de bord). |
|
|
|
|
|
|
|
|
|
|
|
:param depart: Sommet de départ. |
|
|
|
|
|
""" |
|
|
|
|
|
for s in self.sommets: |
|
|
|
|
|
s.cumul = None |
|
|
|
|
|
s.precedent = None |
|
|
|
|
|
sommets = tas.Tas(lambda x: -x.cumul) |
|
|
|
|
|
depart = depart or self.sommets[0] |
|
|
|
|
|
depart.cumul = 0 |
|
|
|
|
|
depart.cle = sommets.ajoute(depart) |
|
|
|
|
|
while not sommets.empty(): |
|
|
|
|
|
s = sommets.pop() |
|
|
|
|
|
for arete in s.aretes: |
|
|
|
|
|
voisin = arete.voisin(s) |
|
|
|
|
|
inf = voisin.cumul is None |
|
|
|
|
|
if inf or s.cumul + arete.cout < voisin.cumul: |
|
|
|
|
|
voisin.cumul = s.cumul + arete.cout |
|
|
|
|
|
if inf: # cumul infini |
|
|
|
|
|
voisin.cle = sommets.ajoute(voisin) |
|
|
|
|
|
else: |
|
|
|
|
|
sommets.actualise(voisin.cle) |
|
|
|
|
|
voisin.precedent = arete |
|
|
|
|
|
|
|
|
|
|
|
def dijkstraPartiel(self, depart, arrivee): |
|
|
|
|
|
"""Calcule les plus courts chemins depuis le sommet `depart` vers |
|
|
|
|
|
`arrivee` (attention, effets de bord). |
|
|
|
|
|
|
|
|
|
|
|
:param depart: Sommet de départ. |
|
|
|
|
|
""" |
|
|
|
|
|
for s in self.sommets: |
|
|
|
|
|
s.cumul = None |
|
|
|
|
|
s.precedent = None |
|
|
|
|
|
sommets = tas.Tas(lambda x: -x.cumul) |
|
|
|
|
|
depart = depart or self.sommets[0] |
|
|
|
|
|
depart.cumul = 0 |
|
|
|
|
|
depart.cle = sommets.ajoute(depart) |
|
|
|
|
|
while not sommets.empty(): |
|
|
|
|
|
s = sommets.pop() |
|
|
|
|
|
for arete in s.aretes: |
|
|
|
|
|
voisin = arete.voisin(s) |
|
|
|
|
|
inf = voisin.cumul is None |
|
|
|
|
|
if inf or s.cumul + arete.cout < voisin.cumul: |
|
|
|
|
|
voisin.cumul = s.cumul + arete.cout |
|
|
|
|
|
if inf: # cumul infini |
|
|
|
|
|
voisin.cle = sommets.ajoute(voisin) |
|
|
|
|
|
else: |
|
|
|
|
|
sommets.actualise(voisin.cle) |
|
|
|
|
|
voisin.precedent = arete |
|
|
|
|
|
|
|
|
|
|
|
def traceArbreDesChemins(self): |
|
|
|
|
|
"""Change la couleur des chemins optimaux en violet-rose |
|
|
|
|
|
(252, 17, 189) et le départ en bleu (10, 98, 252). |
|
|
|
|
|
""" |
|
|
|
|
|
for s in self.sommets: |
|
|
|
|
|
if s.precedent: |
|
|
|
|
|
s.precedent.couleur = (252/255, 17/255, 189/255) |
|
|
|
|
|
if not s.cumul: # sommet de départ |
|
|
|
|
|
s.couleur = (10/255, 98/255, 252/255) |
|
|
|
|
|
|
|
|
|
|
|
def fixeCarburantCommeCout(self): |
|
|
|
|
|
"""Fixe le carburant pour cout lors du calcul de plus court chemin.""" |
|
|
|
|
|
self.cout = Graphe.Cout.CARBURANT |
|
|
|
|
|
|
|
|
|
|
|
def fixeTempsCommeCout(self): |
|
|
|
|
|
"""Fixe le temps pour cout lors du calcul de plus court chemin.""" |
|
|
|
|
|
self.cout = Graphe.Cout.TEMPS |
|
|
|
|
|
|
|
|
|
|
|
def cheminOptimal(self, arrivee): |
|
|
|
|
|
"""Donne le chemin optimal pour aller à `arrivee` depuis le point |
|
|
|
|
|
de départ donné à `dijkstra`. |
|
|
|
|
|
|
|
|
|
|
|
:param arrivee: Sommet d'arrivee |
|
|
|
|
|
|
|
|
|
|
|
:return: le chemin (liste des arêtes) |
|
|
|
|
|
""" |
|
|
|
|
|
chemin = [] |
|
|
|
|
|
suivant = arrivee |
|
|
|
|
|
while suivant.cumul: |
|
|
|
|
|
chemin.append(suivant.precedent) |
|
|
|
|
|
suivant = suivant.precedent.voisin(suivant) |
|
|
|
|
|
chemin.reverse() |
|
|
|
|
|
return chemin |
|
|
|
|
|
|
|
|
|
|
|
def colorieChemin(self, chemin, c): |
|
|
|
|
|
"""Colorie le chemin. |
|
|
|
|
|
|
|
|
|
|
|
:param chemin: une liste d'arrêtes |
|
|
|
|
|
:param c: une couleur |
|
|
|
|
|
""" |
|
|
|
|
|
for a in chemin: |
|
|
|
|
|
a.couleur = c |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class Sommet: |
|
|
class Sommet: |
|
|
"""Implémente un sommet de graphe.""" |
|
|
"""Implémente un sommet de graphe.""" |
|
|
@ -125,6 +249,9 @@ class Sommet: |
|
|
self.pos = pos |
|
|
self.pos = pos |
|
|
self.num = num |
|
|
self.num = num |
|
|
self.couleur = couleur |
|
|
self.couleur = couleur |
|
|
|
|
|
self.aretes = set() |
|
|
|
|
|
self.cumul = None |
|
|
|
|
|
self.precedent = None |
|
|
|
|
|
|
|
|
def __str__(self): |
|
|
def __str__(self): |
|
|
return "v{} (x = {} km y = {} km)".format( |
|
|
return "v{} (x = {} km y = {} km)".format( |
|
|
@ -138,7 +265,7 @@ class Sommet: |
|
|
|
|
|
|
|
|
:param v: Point de calcul de la distance. |
|
|
:param v: Point de calcul de la distance. |
|
|
""" |
|
|
""" |
|
|
return ((self.x()-v.x())**2 + (self.y()-v.y())**2)**(1/2) |
|
|
return ((self.x() - v.x())**2 + (self.y() - v.y())**2)**(1 / 2) |
|
|
|
|
|
|
|
|
def x(self): |
|
|
def x(self): |
|
|
"""Retourne l'abcisse de x.""" |
|
|
"""Retourne l'abcisse de x.""" |
|
|
@ -152,7 +279,8 @@ class Sommet: |
|
|
class Arete: |
|
|
class Arete: |
|
|
"""Implémente une arête de graphe.""" |
|
|
"""Implémente une arête de graphe.""" |
|
|
|
|
|
|
|
|
def __init__(self, s1, s2, longueur, v_moyenne, couleur=(0., 0., 0.)): |
|
|
def __init__( |
|
|
|
|
|
self, s1, s2, longueur, v_moyenne, graph, couleur=(0., 0., 0.)): |
|
|
"""Initialise une arête. |
|
|
"""Initialise une arête. |
|
|
|
|
|
|
|
|
:param s1: sommet 1. |
|
|
:param s1: sommet 1. |
|
|
@ -160,12 +288,16 @@ class Arete: |
|
|
:param longueur: longueur de l'arête. |
|
|
:param longueur: longueur de l'arête. |
|
|
:param v_moyenne: vitesse moyenne sur l'arête. |
|
|
:param v_moyenne: vitesse moyenne sur l'arête. |
|
|
:param couleur: couleur de l'arête. |
|
|
:param couleur: couleur de l'arête. |
|
|
|
|
|
:param graph: le graphe de l'arête. |
|
|
""" |
|
|
""" |
|
|
self.s1 = s1 |
|
|
self.s1 = s1 |
|
|
self.s2 = s2 |
|
|
self.s2 = s2 |
|
|
|
|
|
s1.aretes.add(self) |
|
|
|
|
|
s2.aretes.add(self) |
|
|
self.longueur = longueur |
|
|
self.longueur = longueur |
|
|
self.v_moyenne = v_moyenne |
|
|
self.v_moyenne = v_moyenne |
|
|
self.couleur = couleur |
|
|
self.couleur = couleur |
|
|
|
|
|
self.graph = graph |
|
|
|
|
|
|
|
|
def voisin(self, s): |
|
|
def voisin(self, s): |
|
|
"""Retourne le sommet voisin de s dans l'arête. |
|
|
"""Retourne le sommet voisin de s dans l'arête. |
|
|
@ -177,6 +309,16 @@ class Arete: |
|
|
else: |
|
|
else: |
|
|
return self.s1 |
|
|
return self.s1 |
|
|
|
|
|
|
|
|
|
|
|
@property |
|
|
|
|
|
def cout(self): |
|
|
|
|
|
"""Retourne le cout de l'arête.""" |
|
|
|
|
|
if self.graph.cout is Graphe.Cout.TEMPS: |
|
|
|
|
|
return self.longueur / self.v_moyenne |
|
|
|
|
|
elif self.graph.cout is Graphe.Cout.CARBURANT: |
|
|
|
|
|
return self.longueur |
|
|
|
|
|
else: |
|
|
|
|
|
return 1 |
|
|
|
|
|
|
|
|
def __str__(self): |
|
|
def __str__(self): |
|
|
return " v{v1}, v{v2} (long. = {lon} km vlim. = {v} km/h)".format( |
|
|
return " v{v1}, v{v2} (long. = {lon} km vlim. = {v} km/h)".format( |
|
|
v1=str(self.s1.num), |
|
|
v1=str(self.s1.num), |
|
|
@ -195,7 +337,7 @@ def pointsAleatoires(n, L): |
|
|
""" |
|
|
""" |
|
|
g = Graphe("Graphe aléatoire") |
|
|
g = Graphe("Graphe aléatoire") |
|
|
for _ in range(n): |
|
|
for _ in range(n): |
|
|
x, y = random.uniform(-L/2, L/2), random.uniform(-L/2, L/2) |
|
|
x, y = random.uniform(-L / 2, L / 2), random.uniform(-L / 2, L / 2) |
|
|
g.ajouteSommet(x, y) |
|
|
g.ajouteSommet(x, y) |
|
|
return g |
|
|
return g |
|
|
|
|
|
|
|
|
@ -207,8 +349,8 @@ def test_gabriel(g, v1, v2): |
|
|
:param v1: Premier sommet. |
|
|
:param v1: Premier sommet. |
|
|
:param v2: Deuxième sommet. |
|
|
:param v2: Deuxième sommet. |
|
|
""" |
|
|
""" |
|
|
milieu = Sommet(((v1.x()+v2.x())/2, (v1.y()+v2.y())/2), -1) |
|
|
milieu = Sommet(((v1.x() + v2.x()) / 2, (v1.y() + v2.y()) / 2), -1) |
|
|
rayon = v1.distance(v2)/2 |
|
|
rayon = v1.distance(v2) / 2 |
|
|
ajoute = True |
|
|
ajoute = True |
|
|
for v3 in g.sommets: |
|
|
for v3 in g.sommets: |
|
|
if v3 in [v1, v2]: |
|
|
if v3 in [v1, v2]: |
|
|
|