Está en la página 1de 9

Trabajo Final - Matemáticas

Discretas II
Realizado por Diego Muñoz y Victor Restrepo.

Clases Grafo y vértice:


class vertice:
    def __init__(self, nodo, coordendas):
        self.id = nodo
        self.vecinos = {}
        self.coordenadas = coordendas

    def __str__(self):
        return str(self.id) + ', coordenadas: ' + str(self.coo
rdenadas) + ', vecinos: ' + str([nodo.id for nodo in self.veci
nos])

    def agregar_vecino(self, nodo_vecino, peso = 1):


        self.vecinos[nodo_vecino] = peso

    def get_conecciones(self):
        return self.vecinos.keys()

    def get_id(self):
        return self.id

    def get_peso(self, nodo):


        return self.vecinos[nodo]
    def get_coordenadas(self):
        return self.coordenadas

class grafo:
    def __init__(self):
        self.vertices = {}

    def __iter__(self):
        return iter(self.vertices.values())

    def agregar_vertice(self, nodo, coordenadas):


        nuevo_vertice = vertice(nodo, coordenadas)
        self.vertices[nodo] = nuevo_vertice

    def get_vertice(self, nodo):


        if nodo in self.vertices:
            return self.vertices[nodo]
        else:
            return None

    def agregar_arista(self, a, b, costo = 1):


        self.vertices[a].agregar_vecino(self.vertices[b], cost
o)
        self.vertices[b].agregar_vecino(self.vertices[a], cost
o)

    def get_vertices(self):
        return self.vertices.keys()

Algoritmo A* y sus funciones complementarias:


import math
from Grafo import *

grafo = grafo()

grafo.agregar_vertice('A', (0, 140))


grafo.agregar_vertice('B', (50, 160))
grafo.agregar_vertice('C', (40, 125))
grafo.agregar_vertice('D', (25, 85))
grafo.agregar_vertice('E', (90, 175))
grafo.agregar_vertice('F', (90, 105))
grafo.agregar_vertice('G', (75, 45))
grafo.agregar_vertice('H', (135, 25))
grafo.agregar_vertice('I', (135, 60))
grafo.agregar_vertice('J', (165, 110))
grafo.agregar_vertice('K', (160, 185))
grafo.agregar_vertice('L', (255, 180))
grafo.agregar_vertice('M', (210, 155))
grafo.agregar_vertice('N', (300, 150))
grafo.agregar_vertice('O', (265, 125))
grafo.agregar_vertice('P', (220, 100))
grafo.agregar_vertice('Q', (235, 50))
grafo.agregar_vertice('R', (180, 55))
grafo.agregar_vertice('S', (195, 10))
grafo.agregar_vertice('T', (245, 10))
grafo.agregar_vertice('U', (270, 0))
grafo.agregar_vertice('V', (310, 0))
grafo.agregar_vertice('W', (335, 30))
grafo.agregar_vertice('X', (300, 50))
grafo.agregar_vertice('Y', (270, 95))
grafo.agregar_vertice('Z', (300, 85))
grafo.agregar_vertice('AA', (330, 70))
grafo.agregar_vertice('AB', (320, 110))

#Vecinos
grafo.agregar_arista('A', 'B', 6)
grafo.agregar_arista('A', 'C', 1.4)
grafo.agregar_arista('A', 'D', 2.5)
grafo.agregar_arista('B', 'E', 8)
grafo.agregar_arista('B', 'C', 2)
grafo.agregar_arista('C', 'F', 9.9)
grafo.agregar_arista('D', 'F', 8.2)
grafo.agregar_arista('D', 'G', 20)
grafo.agregar_arista('E', 'F', 7)
grafo.agregar_arista('E', 'K', 16)
grafo.agregar_arista('F', 'I', 14)
grafo.agregar_arista('F', 'J', 16.9)
grafo.agregar_arista('F', 'K', 18)
grafo.agregar_arista('G', 'H', 14)
grafo.agregar_arista('G', 'I', 18)
grafo.agregar_arista('H', 'I', 16)
grafo.agregar_arista('H', 'S', 8.5)
grafo.agregar_arista('I', 'J', 9)
grafo.agregar_arista('I', 'R', 10)
grafo.agregar_arista('J', 'K', 16)
grafo.agregar_arista('J', 'P', 6.5)
grafo.agregar_arista('J', 'R', 8)
grafo.agregar_arista('K', 'L', 20)
grafo.agregar_arista('K', 'M', 4)
grafo.agregar_arista('L', 'M', 8)
grafo.agregar_arista('L', 'N', 4)
grafo.agregar_arista('M', 'P', 5)
grafo.agregar_arista('N', 'O', 1)
grafo.agregar_arista('N', 'AB', 10)
grafo.agregar_arista('O', 'P', 4.3)
grafo.agregar_arista('O', 'Y', 3.5)
grafo.agregar_arista('P', 'Q', 9)
grafo.agregar_arista('Q', 'R', 11)
grafo.agregar_arista('Q', 'T', 12)
grafo.agregar_arista('Q', 'Y', 2)
grafo.agregar_arista('R', 'S', 12)
grafo.agregar_arista('S', 'T', 4.3)
grafo.agregar_arista('T', 'U', 7)
grafo.agregar_arista('U', 'Y', 15.2)
grafo.agregar_arista('U', 'V', 1.7)
grafo.agregar_arista('V', 'W', 1.5)
grafo.agregar_arista('V', 'X', 15.3)
grafo.agregar_arista('W', 'AA', 3)
grafo.agregar_arista('X', 'Z', 2.8)
grafo.agregar_arista('X', 'AA', 5)
grafo.agregar_arista('Y', 'Z', 1)
grafo.agregar_arista('Z', 'AB', 2)
grafo.agregar_arista('AA', 'AB', 4)

start = grafo.vertices['A']
goal = grafo.vertices['W']

#La función de heurística que se escogió fue la métrica euclid


eana.
#Nótese que estamos comparando siempre con las coordenadas de
goal.
def heuristic_function(vertice1):
    return math.sqrt((vertice1.get_coordenadas()[0]-goal.get_c
oordenadas()[0])**2+(vertice1.get_coordenadas()[1]-goal.get_co
ordenadas()[1])**2)

#Reconstruye el camino recorrido almacenado en cameFrom y en e


l proceso también calcula el costo del mismo.
def reconstruct_path(cameFrom, current):
    total_path = [current.get_id()]
    costo = 0
    while current in cameFrom.keys():
        costo += current.get_peso(cameFrom[current])
        current = cameFrom[current]
        total_path.append(current.get_id())
    return (total_path[::-1], costo)
#Función auxiliar que retorna el elmento dentro del openSet co
n el fScore más pequeño
def min(openSet, fScore):
    min_element = None
    min_value = math.inf
    for element in openSet:
        if element in fScore:
            if fScore[element] < min_value:
                min_element = element
                min_value = fScore[element]
    return min_element

#El algoritmo se ejecuta mientras hayan elementos dentro del o


penset, el cual representa los nodos por los cuales no se ha e
xplorado.
#gScore y fScore son diccionarios que representan el costo rea
l y el costo real más la heurística respectivamente.
def A_star_search(grafo, start, goal):
    open_set = [start]
    came_from = {}
    gScore = {start: 0}
    fScore = {start: heuristic_function(start)}

    while open_set:
        vertice_actual = min(open_set, fScore)
        if vertice_actual is goal:
            return reconstruct_path(came_from, vertice_actual)

        open_set.remove(vertice_actual)
        for vecino in vertice_actual.vecinos.keys():
            tentative_gScore = gScore[vertice_actual] + vertic
e_actual.get_peso(vecino)
            if tentative_gScore < gScore.get(vecino, math.in
f):
                came_from[vecino] = vertice_actual
                gScore[vecino] = tentative_gScore
                fScore[vecino] = gScore[vecino] + heuristic_fu
nction(vecino)
                if vecino not in open_set:
                    open_set.append(vecino)

    return -1

path, costo = A_star_search(grafo, start, goal)

print("Grafo: ")
for vertice in grafo.vertices.values():
    print(vertice)
print(" ")
print("Camino más óptimo desde " + str(start.get_id()) + " has
ta " + str(goal.get_id()) + ":")
print(" -> ".join(path))
print("Costo del viaje: " + str(costo))

Ejecución del programa:


-- Ejecución con heurística igual a la métrica Euclideana — 
 

Comentarios:
La heurística utilizada fue la métrica Euclideana.
Al investigar sobre el algoritmo encontramos que la función heurística debería
siempre de arrojar un valor a lo sumo igual al costo real, pero dadas estas
coordenadas esta desigualdad no se cumplía, en otras palabras, es posible que el
camino encontrado por el algoritmo no sea el más optimo. 
El problema anterior es difícil de solucionar basándose en las coordenadas del
ejercicio como heurística porque de hecho la métrica Euclideana era la función
que más bajos resultados arrojaba a comparación de la métrica Manhattan por
ejemplo. 
Por lo anteriormente descrito es normal que al usar la función nula como métrica
obtengamos un camino con costo absoluto real menor al arrojado con la métrica
Euclideana como se ve en la siguiente imagen:
-- Ejecución con heurística igual a la función nula — 

Conclusión:
La función heurística desempeña un papel fundamental en los resultados del
algoritmo, por esto mismo se hace necesario utilizar una que cumpla la desigualdad
anteriormente descrita o en su defecto utilizar la función nula.

También podría gustarte