|
|
|
@ -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,6 +81,9 @@ class Graphe: |
|
|
|
for s in self.sommets: |
|
|
|
dest.changeCouleur(s.couleur) |
|
|
|
dest.tracePoint((s.x(), s.y())) |
|
|
|
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: |
|
|
|
@ -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( |
|
|
|
|