diff --git a/busquedas.py b/busquedas.py index a52165c..5304136 100755 --- a/busquedas.py +++ b/busquedas.py @@ -99,14 +99,19 @@ def es_meta(estado): class Nodo: """ Clase para implementar un árbol como estructura de datos. - """ - def __init__(self, estado, accion=None, padre=None, costo_local=0): + def __init__(self, estado, problema, accion=None, padre=None, costo_local=0): """ Inicializa un nodo como una estructura + @param estado: El estado del nodo. + @param problema: El problema que se está resolviendo. + @param accion: La acción que llevó a este nodo. + @param padre: El nodo padre. + @param costo_local: El costo local de alcanzar este nodo. """ self.estado = estado + self.problema = problema self.accion = accion self.padre = padre self.costo = 0 if not padre else padre.costo + costo_local @@ -120,11 +125,11 @@ def expande(self, modelo): @param modelo: Un objeto de una clase heredada de ModeloBusqueda @return: Una lista de posibles nodos sucesores - """ return ( Nodo( modelo.sucesor(self.estado, a), + self.problema, # Pasamos el problema a los nodos hijos a, self, modelo.costo_local(self.estado, a)) @@ -134,8 +139,7 @@ def genera_plan(self): """ Genera el plan (parcial o completo) que representa el nodo. - @return: Una lista [(x0, c0), a1, (x1, c1), a2, (x2, c2), ..., aT, (xT, cT)], donde los x0, x1, ..., xT son tuplas con los estados a cada paso del plan, c0, c1, ..., cT es el costo total hasta ese momento del plan, a1, a2, ..., aT son las acciónes que hay que implementar para llegar desde el estado inicial x0 hasta el testado final xT - + @return: Una lista con el plan generado """ return ([(self.estado, self.costo)] if not self.padre else (self.padre.genera_plan() @@ -144,7 +148,6 @@ def genera_plan(self): def __str__(self): """ Muestra el nodo como lo que es en realidad, un plan. - """ plan = self.genera_plan() return (f"Costo: {self.costo}\n" + @@ -154,12 +157,14 @@ def __str__(self): for (x, a, xp) in zip(plan[:-1:2], plan[1::2], plan[2::2])])) - # Este método de sobrecarga del operador < es necesario - # para poder utilizar los nodos en la heapq def __lt__(self, other): + """ + Sobrecarga del operador < para poder usar los nodos en la heapq. + """ return self.profundidad < other.profundidad + def busqueda_ancho(problema): """ Búsqueda a lo ancho para un problema de búsquedas dado @@ -283,5 +288,21 @@ def busqueda_A_estrella(problema, heuristica): @return Un objeto tipo Nodo con la estructura completa """ - raise NotImplementedError('Hay que hacerlo de tarea \ - (problema 2 en el archivo busquedas.py)') + frontera = [] + nodo_inicial = Nodo(problema.x0) + heapq.heappush(frontera, (heuristica(nodo_inicial),nodo_inicial)) + visitados = {problema.x0: 0} + + while frontera: + _, nodo = heapq.heappop(frontera) # Extrae el nodo con menor f(n) + + if problema.es_meta(nodo.estado): # Si es la meta, regresa la solución + nodo.nodos_visitados = problema.num_nodos + return nodo + + for hijo in nodo.expande(problema.modelo): + costo_hijo = hijo.costo + heuristica(hijo) # f(n) = g(n) + h(n) + + if hijo.estado not in visitados or visitados[hijo.estado] > hijo.costo: + heapq.heappush(frontera, (costo_hijo, hijo)) + visitados[hijo.estado] = hijo.costo diff --git a/problemas.py b/problemas.py index b6b0bd1..9f32f26 100755 --- a/problemas.py +++ b/problemas.py @@ -10,14 +10,12 @@ import busquedas - - # ------------------------------------------------------------ # Desarrolla el modelo del Camión mágico # ------------------------------------------------------------ -class CamionMagico.busquedas.ModeloBusqueda): - """ +class CamionMagico(busquedas.ModeloBusqueda): + """ --------------------------------------------------------------------------------- Supongamos que quiero trasladarme desde la posición discreta $1$ hasta la posicion discreta $N$ en una vía recta usando un camión mágico. @@ -28,42 +26,48 @@ class CamionMagico.busquedas.ModeloBusqueda): de 2 minutos. Desarrollar la clase del modelo del camión mágico - ---------------------------------------------------------------------------------- - + ---------------------------------------------------------------------------------- """ - def __init__(self): - raise NotImplementedError('Hay que hacerlo de tarea') + def __init__(self, N): + self.N = N def acciones_legales(self, estado): - raise NotImplementedError('Hay que hacerlo de tarea') + acciones = [] + if estado + 1 <= self.N: + acciones.append("caminar") + if 2 * estado <= self.N: + acciones.append("saltar") + return acciones def sucesor(self, estado, accion): - raise NotImplementedError('Hay que hacerlo de tarea') + """Devolver el estado al hacer una accion""" + if accion == "caminar": + return estado + 1 + elif accion == "saltar": + return 2 * estado def costo_local(self, estado, accion): - raise NotImplementedError('Hay que hacerlo de tarea') + """costo de la accion""" + return 1 if accion == "caminar" else 2 @staticmethod def bonito(estado): """ El prettyprint de un estado dado - """ raise NotImplementedError('Hay que hacerlo de tarea') - + # ------------------------------------------------------------ # Desarrolla el problema del Camión mágico # ------------------------------------------------------------ -class PblCamionMágico(busquedas.ProblemaBusqueda): +class PblCamionMagico(busquedas.ProblemaBusqueda): """ El problema a resolver es establecer un plan para ir desde el punto $1$ hasta el punto $N$ en el menor tiempo posible. - """ - def __init__(self): - raise NotImplementedError('Hay que hacerlo de tarea') - + def __init__(self, N): + super().__init__(1, lambda estado: estado == N, CamionMagico(N)) # ------------------------------------------------------------ # Desarrolla una política admisible. @@ -73,10 +77,8 @@ def h_1_camion_magico(nodo): """ DOCUMENTA LA HEURÍSTICA QUE DESARROLLES Y DA UNA JUSTIFICACIÓN PLATICADA DE PORQUÉ CREES QUE LA HEURÍSTICA ES ADMISIBLE - """ - return 0 - + return nodo.problema.N - nodo.estado # Distancia hasta la meta # ------------------------------------------------------------ # Desarrolla otra política admisible. @@ -88,15 +90,15 @@ def h_2_camion_magico(nodo): """ DOCUMENTA LA HEURÍSTICA DE DESARROLLES Y DA UNA JUSTIFICACIÓN PLATICADA DE PORQUÉ CREES QUE LA HEURÍSTICA ES ADMISIBLE - """ - return 0 + x = nodo.estado + return (nodo.problema.N - x) // x # Estimación de número de saltos # ------------------------------------------------------------ # Desarrolla el modelo del cubo de Rubik # ------------------------------------------------------------ -class CuboRubik.busquedas.ModeloBusqueda): +class CuboRubik(busquedas.ModeloBusqueda): """ La clase para el modelo de cubo de rubik, documentación, no olvides poner la documentación de forma clara y concisa. @@ -120,50 +122,45 @@ def costo_local(self, estado, accion): def bonito(estado): """ El prettyprint de un estado dado - """ raise NotImplementedError('Hay que hacerlo de tarea') - - # ------------------------------------------------------------ + +# ------------------------------------------------------------ # Desarrolla el problema del Cubo de Rubik # ------------------------------------------------------------ class PblCuboRubik(busquedas.ProblemaBusqueda): """ El problema a resolver es establecer un plan para resolver el cubo de rubik. - """ def __init__(self): raise NotImplementedError('Hay que hacerlo de tarea') - # ------------------------------------------------------------ # Desarrolla una política admisible. # ------------------------------------------------------------ -def h_1_problema_1(nodo): + +def h_1_camion_magico(nodo): """ DOCUMENTA LA HEURÍSTICA QUE DESARROLLES Y DA UNA JUSTIFICACIÓN PLATICADA DE PORQUÉ CREES QUE LA HEURÍSTICA ES ADMISIBLE - """ - return 0 - + problema = nodo.problema + return problema.N - nodo.estado # Distancia hasta la meta # ------------------------------------------------------------ # Desarrolla otra política admisible. # Analiza y di porque piensas que es (o no es) dominante una # respecto otra política # ------------------------------------------------------------ + def h_2_problema_1(nodo): """ DOCUMENTA LA HEURÍSTICA DE DESARROLLES Y DA UNA JUSTIFICACIÓN PLATICADA DE PORQUÉ CREES QUE LA HEURÍSTICA ES ADMISIBLE - """ return 0 - - def compara_metodos(problema, heuristica_1, heuristica_2): """ Compara en un cuadro lo nodos expandidos y el costo de la solución @@ -177,7 +174,6 @@ def compara_metodos(problema, heuristica_1, heuristica_2): Si la búsqueda no informada es muy lenta, posiblemente tendras que quitarla de la función - """ solucion1 = busquedas.busqueda_A_estrella(problema, heuristica_1) solucion2 = busquedas.busqueda_A_estrella(problema, heuristica_2) @@ -193,17 +189,13 @@ def compara_metodos(problema, heuristica_1, heuristica_2): + str(solucion2.nodos_visitados)) print('-' * 50 + '\n\n') - if __name__ == "__main__": - - # Compara los métodos de búsqueda para el problema del camión mágico # con las heurísticas que desarrollaste - problema = PblCamionMágico( XXXXXXXXXX ) # <--- PONLE LOS PARÁMETROS QUE NECESITES + problema = PblCamionMagico(10) # Ejemplo con N=10 compara_metodos(problema, h_1_camion_magico, h_2_camion_magico) # Compara los métodos de búsqueda para el problema del cubo de rubik # con las heurísticas que desarrollaste - problema = PblCuboRubik( XXXXXXXXXX ) # <--- PONLE LOS PARÁMETROS QUE NECESITES + problema = PblCuboRubik() # Asegúrate de tener los parámetros necesarios compara_metodos(problema, h_1_problema_1, h_2_problema_1) - \ No newline at end of file