This commit is contained in:
Klafyvel 2018-01-12 17:57:17 +01:00
parent 3c48f975b2
commit 92ae9237d3
5 changed files with 197 additions and 24 deletions

BIN
compte_rendu/images/3_7.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

127
graphe.py
View file

@ -1,4 +1,7 @@
import sys
import random
import numpy as np
import triangulation
import tas
@ -18,7 +21,8 @@ class Graphe:
self.nom = nom
self.sommets = []
self.aretes = []
self.cout = Graphe.Cout.TEMPS
self.cout = None, Graphe.Cout.TEMPS
self.arrivee = None
def renomme(self, nom):
"""Renome le graphe
@ -35,7 +39,7 @@ class Graphe:
:return: Le sommet créé.
"""
s = Sommet((x, y), len(self.sommets))
s = Sommet((x, y), len(self.sommets), graphe=self)
self.sommets.append(s)
return s
@ -77,7 +81,10 @@ class Graphe:
for s in self.sommets:
dest.changeCouleur(s.couleur)
dest.tracePoint((s.x(), s.y()))
dest.traceTexte((s.x(), s.y()), 'v' + str(s.num))
if hasattr(s, 'nom'):
dest.traceTexte((s.x(), s.y()), s.nom)
else:
dest.traceTexte((s.x(), s.y()), 'v' + str(s.num))
for a in self.aretes:
dest.changeCouleur(a.couleur)
@ -166,7 +173,7 @@ class Graphe:
sommets.actualise(voisin.cle)
voisin.precedent = arete
def dijkstraPartiel(self, depart, arrivee):
def dijkstraPartiel(self, depart=None, arrivee=None):
"""Calcule les plus courts chemins depuis le sommet `depart` vers
`arrivee` (attention, effets de bord).
@ -177,20 +184,63 @@ class Graphe:
s.precedent = None
sommets = tas.Tas(lambda x: -x.cumul)
depart = depart or self.sommets[0]
arrivee = arrivee or self.sommets[-1]
depart.cumul = 0
depart.cle = sommets.ajoute(depart)
while not sommets.empty():
n_visite = 0
quit = False
while not (sommets.empty() or quit):
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
voisin.precedent = arete
if inf: # cumul infini
n_visite += 1
voisin.couleur = (0.5, 0.9, 0.1)
if voisin == arrivee:
quit = True
voisin.cle = sommets.ajoute(voisin)
else:
sommets.actualise(voisin.cle)
return self.cheminOptimal(arrivee), n_visite
def astar(self, depart=None, arrivee=None):
"""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
self.arrivee = arrivee or self.sommets[-1]
arrivee = self.arrivee
sommets = tas.Tas(lambda x: -(x.cumul + x.minCumulRestant))
depart = depart or self.sommets[0]
depart.cumul = 0
depart.cle = sommets.ajoute(depart)
n_visite = 0
quit = False
while not (sommets.empty() or quit):
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
voisin.precedent = arete
if inf: # cumul infini
n_visite += 1
voisin.couleur = (0.5, 0.9, 0.1)
if voisin == arrivee:
quit = True
voisin.cle = sommets.ajoute(voisin)
else:
sommets.actualise(voisin.cle)
return self.cheminOptimal(arrivee), n_visite
def traceArbreDesChemins(self):
"""Change la couleur des chemins optimaux en violet-rose
@ -198,9 +248,9 @@ class Graphe:
"""
for s in self.sommets:
if s.precedent:
s.precedent.couleur = (252/255, 17/255, 189/255)
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)
s.couleur = (10 / 255, 98 / 255, 252 / 255)
def fixeCarburantCommeCout(self):
"""Fixe le carburant pour cout lors du calcul de plus court chemin."""
@ -235,11 +285,60 @@ class Graphe:
for a in chemin:
a.couleur = c
def matriceCout(self, tournee):
"""Calcule la matrice de cout d'une tournée.
:param tournee:La liste des étapes avec en première position le départ.
"""
n = len(tournee)
matrice = np.zeros((n, n))
for x, sx in enumerate(tournee):
self.dijkstra(sx)
for y, sy in enumerate(tournee):
matrice[x, y] = matrice[y, x] = sy.cumul
return matrice
def voyageurDeCommerceNaif(self, tournee):
meilleurItineraire = []
minCout = sys.float_info.max
matrice = self.matriceCout(tournee)
cout = 0
visite = [False]*len(tournee)
visite[0] = True
itineraire = [0]
def backtrack():
nonlocal meilleurItineraire, minCout, matrice, cout, visite, itineraire
if cout < minCout:
if len(itineraire)==len(tournee):
if cout + matrice[0, itineraire[-1]] < minCout:
minCout = cout + matrice[0, itineraire[-1]]
meilleurItineraire = [tournee[x] for x in itineraire]
else:
for i,sommet_visite in enumerate(visite):
if not sommet_visite:
visite[i] = True
indicePrec = itineraire[-1]
itineraire.append(i)
cout += matrice[i, indicePrec]
backtrack()
visite[i] = False
cout -= matrice[i, indicePrec]
itineraire.pop()
backtrack()
return minCout, meilleurItineraire
def traceItineraire(self, itineraire):
for i,x in enumerate(itineraire):
x.nom = "s" + str(i)
suivant = itineraire[(i+1)%len(itineraire)]
c,_ = self.astar(x, suivant)
self.colorieChemin(c, (0.8, 0.1, 0.8))
class Sommet:
"""Implémente un sommet de graphe."""
def __init__(self, pos, num, couleur=(0., 0., 0.)):
def __init__(self, pos, num, couleur=(0., 0., 0.), graphe=None):
"""Initialise un sommet.
:param pos: couple donnant la position du point.
@ -252,6 +351,18 @@ class Sommet:
self.aretes = set()
self.cumul = None
self.precedent = None
self.graphe = graphe
@property
def minCumulRestant(self):
x, y = self.graphe.arrivee.pos
d = ((self.pos[0] - x)**2 + (self.pos[1] - y)**2)**(1 / 2)
if self.graphe.cout is Graphe.Cout.TEMPS:
return d / 90 # On minore le temps en divisant par la vitesse max
elif self.graphe.cout is Graphe.Cout.CARBURANT:
return d
else:
return 0
def __str__(self):
return "v{} (x = {} km y = {} km)".format(

94
test.py
View file

@ -269,6 +269,84 @@ def testQuestion3_6():
pl.show()
def testQuestion3_7():
"""Trace le plus court chemin avec dijkstra."""
g = graphe.pointsAleatoires(30, 30)
g.fixeTempsCommeCout()
graphe.reseauRapide(g)
g.dijkstraPartiel(g.sommets[0], g.sommets[-1])
g.traceArbreDesChemins()
g.renomme("Dijkstra partiel")
graphique.affiche(g, (0, 0), 10.)
def testQuestion3_8():
"""Trace le plus court chemin avec dijkstra."""
g = graphe.pointsAleatoires(30, 30)
g.fixeCarburantCommeCout()
graphe.reseauRapide(g)
g.astar(g.sommets[0], g.sommets[-1])
g.traceArbreDesChemins()
g.renomme("A*")
graphique.affiche(g, (0, 0), 10., blocage=False)
g.dijkstraPartiel(g.sommets[0], g.sommets[-1])
g.traceArbreDesChemins()
g.renomme("Dijkstra partiel")
graphique.affiche(g, (0, 0), 10.)
def testQuestion3_9():
"""Compare Dijkstra avec et sans tas et A*."""
prepare = lambda p : graphe.reseauRapide(graphe.pointsAleatoires(p, 10))
valeurs_n = list(map(lambda x: int(x), np.logspace(1, 3, 50)))
temps1 = chronometre(graphe.Graphe.dijkstra, prepare, valeurs_n)
temps2 = chronometre(graphe.Graphe.dijkstraPartiel, prepare, valeurs_n)
temps3 = chronometre(graphe.Graphe.astar, prepare, valeurs_n)
pl.close('all')
pl.title("Comparaison du temps d'exécution de `dijkstra`, `dijkstraPartiel` et `astar`.")
pl.plot(valeurs_n, temps1, label='Dijkstra')
pl.plot(valeurs_n, temps2, label='Dijkstra partiel avec tas')
pl.plot(valeurs_n, temps3, label='A*')
pl.legend(loc='best')
pl.xlabel("n")
pl.ylabel("temps")
pl.show()
pl.title("Comparaison du temps d'exécution de `dijkstra`, `dijkstraPartiel` et `astar`.")
pl.loglog(valeurs_n, temps1, label='Dijkstra')
pl.loglog(valeurs_n, temps2, label='Dijkstra partiel avec tas')
pl.loglog(valeurs_n, temps3, label='A*')
pl.legend(loc='best')
pl.xlabel("n")
pl.ylabel("temps")
pl.show()
def testQuestion4_1():
"""Trace le plus court chemin avec dijkstra."""
g = graphe.pointsAleatoires(30, 30)
graphe.reseauRapide(g)
g.renomme("graphe")
print("Points 0,1 et 2")
tournee = [g.sommets[i] for i in (0, 1, 2)]
print(g.matriceCout(tournee))
graphique.affiche(g, (0, 0), 10.)
def testQuestion4_2():
g = graphe.pointsAleatoires(30, 30)
graphe.reseauRapide(g)
g.renomme("graphe")
print("Points 0,1,2 et 7")
tournee = [g.sommets[i] for i in (0, 1, 2, 7)]
cout,iti = g.voyageurDeCommerceNaif(tournee)
g.traceItineraire(iti)
graphique.affiche(g, (0, 0), 10.)
if __name__ == '__main__':
n = sys.argv[1]
if n in ('--help', '-h'):
@ -276,19 +354,3 @@ if __name__ == '__main__':
else:
print('Question ', n)
locals()['testQuestion'+n]()
# testQuestion1_2()
# testQuestion1_3()
# testQuestion1_4()
# testQuestion2_2()
# testQuestion2_4()
# testQuestion2_5()
# testQuestion2_6()
# testQuestion2_7()
# testQuestion2_8()
# testQuestion2_9()
# testQuestion3_1()
# testQuestion3_2()
# testQuestion3_3()
# testQuestion3_4()
# testQuestion3_5()
# testQuestion3_6()