You are on page 1of 7

Prctica 3

Bsqueda

Vamos a continuar viendo algoritmos de bsqueda en grafos. En esta prctica nos centraremos en algoritmos de bsqueda informada.

1 Heurstico
Ya hemos visto en teora que este tipo de algoritmos necesitan lo que se conoce como funcin heurstica. Esta funcin debe ser una estimacin del coste que supone ir de un nodo cualquiera al nodo objetivo. Pensando en el problema de las ciudades, una buena funcin heurstica sera la distancia que hay en lnea recta desde cada ciudad a la ciudad objetivo (ese ha sido precisamente el heurstico utilizado). Trabajo: En la prctica anterior adaptaste el fichero CiudadesCompleto.java para resolver el problema de las ciudades en Rumana. En aquel momento pusiste como heurstico 0 para todas las ciudades. Modifica, ahora, el valor del heurstico para adaptarlo a lo comentado en el prrafo anterior.

2 A* y primero el mejor
Podemos ahora llamar a los algoritmos de bsqueda informada implementados en la librera busqueda.jar (actualmente, esta librera tiene implementados los algoritmos A* y primero el mejor). Para ello debemos aadir el siguiente cdigo:
System.out.println("--------------------------------------------"); inicio = System.currentTimeMillis(); sol = PrimeroElMejor.Resuelve(a,"ciudades_primero_el_mejor.xml"); fin = System.currentTimeMillis(); System.out.println("Una solucin por Primero el Mejor: "+sol); System.out.println("Y ha tardado: "+ ((fin-inicio)/1000.0) +" segundos"); System.out.println("--------------------------------------------"); inicio = System.currentTimeMillis(); sol = AEstrella.Resuelve(a,"ciudades_aestrella.xml"); fin = System.currentTimeMillis(); System.out.println("Una solucin por A*: "+sol); System.out.println("Y ha tardado: "+ ((fin-inicio)/1000.0) +" segundos"); System.out.println("Numero de pasos para la solucin: "+sol.size());

Este cdigo ejecuta ambos algoritmos ofreciendo, al finalizar, el camino propuesto por cada uno y el tiempo que han empleado para su clculo. Ahora ya podemos compilar y ejecutar:
javac classpath busqueda.jar CiudadesCompleto.java java classpath busqueda.jar;. CiudadesCompleto

Trabajo: Utiliza estos dos algoritmos para resolver los dos problemas de las ciudades planteados Obtienen mejores caminos? Son ms rpidos? Visualiza las trazas. Puedes Hacer lo mismo con el fichero CiudadesCompleto.java original (ya tena un heurstico para el problema original)

3 NPuzzle
Vamos a trabajar ahora con el famoso 8-puzzle. Ya sabis que el 8-puzzle es un juego en el que hay 9 casillas y 8 fichas. El objetivo del juego es pasar de un estado inicial de las fichas a uno final mediante sucesivos movimientos de las diferentes fichas hacia la casilla vaca.

Imaginemos los siguientes estados inicial y final. Qu movimientos son necesarios para llegar alcanzar el objetivo? Estado Inicial 1 3 6 5 2 8 4 7 Estado Final 1 2 3 4 5 6 7 8

Para solucionar este problema podemos utilizar los algoritmos de bsqueda incluidos en la librera busqueda.jar. Para ello necesitamos definir la clase NPuzzle. Esta clase podemos descargarla del siguiente enlace: http://www.aic.uniovi.es/ssii/P3/NPuzzle.java

3.1 Cdigo
Para codificar este problema necesitamos cuatro campos:
private int[][] puzzle; static private int[][]obje; private int fi; private int co;

puzzle indica la situacin del puzzle en el estado actual, obje la situacin de las piezas en el objetivo y los campos fi y co se utilizan para almacenar la posicin en la que no hay pieza. Se necesitan dos constructores: uno para instanciar el problema y otro para ir generando los nodos sucesores:
public NPuzzle(int[][] matriz, int[][] obj){ puzzle = matriz; obje = obj; nombre = new String("N-Puzzle"); prof=0; for (int i=0; i < matriz.length; ++i){ for (int j=0; j < matriz[i].length; ++j){ if (matriz[i][j]==0){ fi = i; co = j; } } } } private NPuzzle (int[][] matriz, int p, int f, int c){ puzzle = matriz; prof = p; fi = f; co = c; }

Para saber si un nodo es objetivo, lo nico que hay que hacer es ver si puzzle y obje tienen las piezas dispuestas de la misma manera:
public boolean EsObjetivo(){ if (obje.length!=puzzle.length) return false; for (int i=0; i < obje.length; ++i) for (int j=0; j < obje[i].length; ++j) if (obje[i][j] != puzzle[i][j]) return false; return true; }

Para generar nodos sucesores, debemos crear una LinkedList de InfNodos. Un InfNodo contiene el nodo sucesor, el coste para ir del nodo actual al sucesor y un texto descriptivo de la accin. En el NPuzzle debemos controlar la posicin sin ficha para saber qu sucesores tiene el nodo actual:
public LinkedList<InfNodo> Sucesores(){ LinkedList<InfNodo> succ = new LinkedList<InfNodo>(); if((fi+1)<puzzle.length){ int[][] copia = new int[puzzle.length][puzzle[0].length]; for (int i=0; i < puzzle.length; ++i) for (int j=0; j < puzzle[i].length; ++j) copia[i][j]=puzzle[i][j]; copia[fi][co] = copia[fi+1][co]; copia[fi+1][co] = 0; NPuzzle t = new NPuzzle(copia,prof+1,fi+1,co); succ.add(new InfNodo(t,1,new String("Mover " + copia[fi][co] +" hacia arriba"))); } if((co+1)<puzzle[0].length){ int[][] copia = new int[puzzle.length][puzzle[0].length]; for (int i=0; i < puzzle.length; ++i) for (int j=0; j < puzzle[i].length; ++j) copia[i][j]=puzzle[i][j]; copia[fi][co] = copia[fi][co+1]; copia[fi][co+1] = 0; NPuzzle t = new NPuzzle(copia,prof+1,fi,co+1); succ.add(new InfNodo(t,1,new String("Mover " + copia[fi][co] + " hacia izquierda"))); } if((fi-1)>=0){ int[][] copia = new int[puzzle.length][puzzle[0].length]; for (int i=0; i < puzzle.length; ++i) for (int j=0; j < puzzle[i].length; ++j) copia[i][j]=puzzle[i][j]; copia[fi][co] = copia[fi-1][co]; copia[fi-1][co] = 0; NPuzzle t = new NPuzzle(copia,prof+1,fi-1,co); succ.add(new InfNodo(t,1,new String("Mover " + copia[fi][co] + " hacia abajo"))); } if((co-1)>=0){ int[][] copia = new int[puzzle.length][puzzle[0].length]; for (int i=0; i < puzzle.length; ++i) for (int j=0; j < puzzle[i].length; ++j) copia[i][j]=puzzle[i][j]; copia[fi][co] = copia[fi][co-1]; copia[fi][co-1] = 0; NPuzzle t = new NPuzzle(copia,prof+1,fi,co-1); succ.add(new InfNodo(t,1,new String("Mover " + copia[fi][co] + " hacia derecha"))); } return succ; }

El heurstico utilizado para este problema es la suma de los movimientos de todas las piezas para alcanzar sus posiciones objetivo, conocida como la distancia Manhattan:
public double Heuristico(){ // Algoritmo que calcula la suma de las distancias manhattan // hasta la solucion double suma=0; for (int i=0; i < puzzle.length; ++i){ for (int j=0; j < puzzle[i].length; ++j){ if(obje[i][j]!=puzzle[i][j]){ //Buscamos la posicin correcta para la ficha en puzzle[i][j] int i2=0, j2=0; while(i2<obje.length && (obje[i2][j2]!=puzzle[i][j])){ j2++; if (j2 >= obje[i2].length) {i2++; j2=0;} } //En [i2][j2] est la posicin correcta suma+=Math.abs(i2-i)+Math.abs(j2-j); } } } return suma; }

Cuando se crea una nueva clase en java que herede de java.lang.Object es necesario redefinir la funcin equals y la funcin hashCode. Esta ltima es necesaria para optimizar las ordenaciones de objetos. Debemos implementarla de manera que retorne un mismo entero para todos los nodos que sean iguales. En este caso usamos la siguiente codificacin: Nodo 1 3 6 136 * 1 5 2 8 + 528 * 2 4 7 470 * 3 2462
public int hashCode(){ int suma = 0, num; for (int i=0; i < puzzle.length; ++i){ num = 0; for (int j=0; j < puzzle[i].length; ++j) num += puzzle[i][j] * Math.pow(10,puzzle[i].length-(j+1)); suma+=num*(i+1); } return suma; }

Redefinimos tambin la funcin equals:


public boolean equals( Object c ){ if ( c instanceof NPuzzle ){ NPuzzle n = (NPuzzle)c; if (n.puzzle.length!=puzzle.length) return false; for (int i=0; i < puzzle.length; ++i) for (int j=0; j < puzzle[i].length; ++j) if (n.puzzle[i][j]!=puzzle[i][j]) return false; return true; } return false; }

Implementamos, tambin, la funcin Informacin. En este caso, la informacin que mostramos de cada nodo es la situacin de las piezas en el nodo actual y la situacin de las piezas que se quiere alcanzar, es decir, el nodo objetivo.
public String Informacion(){ String retorno = new String(); for (int i=0; i < puzzle.length; ++i){ for (int j=0; j < puzzle[i].length; ++j) retorno= retorno + new String(puzzle[i][j]+" "); retorno = retorno + new String(" "); for (int j=0; j < obje[i].length; ++j) retorno= retorno + new String(obje[i][j]+" "); if(i!=(puzzle.length-1)) retorno= retorno + System.getProperty("line.separator"); } return retorno; }

Y con esto ya tenemos definido todo lo necesario para solucionar el problema del 8-puzzle. Ahora ya podemos compilar y ejecutar:
javac classpath busqueda.jar NPuzzle.java java classpath busqueda.jar;. NPuzzle

Trabajo: Observa el tiempo que tarda en finalizar cada algoritmo Tardan menos los de bsqueda informada? Obtienen todos los algoritmos la misma solucin? Visualiza las diferentes trazas para comprender el funcionamiento de los algoritmos. Cambia los estados inicial y final.

3.2 Cambiando la funcin heurstica y la dimensin


En la clase terica se os ha comentado que hay dos heursticos tradicionales para el problema del NPuzzle. Uno es la suma de los movimientos de todas las piezas para alcanzar sus posiciones objetivo (distancia Manhattan) y el otro cuenta el nmero de piezas descolocadas respecto a su posicin en el objetivo. Trabajo: Implementa el segundo heurstico y compara los tiempos de los algoritmos informados con ambos heursticos. Qu heurstico ofrece mejores soluciones? Hasta ahora hemos trabajado en el problema del NPuzzle tratando de solucionar nicamente el problema del 8-puzzle. Sin embargo, si nos fijamos en la implementacin, parece que no hay limitacin en cuanto a la dimensin del puzzle. Te sientes capaz de adaptarlo para que resuelva el problema del 15-puzzle? Estado Inicial 5 1 3 4 2 6 8 9 10 7 11 13 14 15 12 Estado Final 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

Trabajo: Intntalo! Los cambios que hay que hacer son mnimos. nicamente tienes que modificar el programa principal

4 Otros Problemas
Tambin se ha visto en teora el problema de las NReinas. Este problema consiste en colocar n reinas en un tablero de dimensin n por n sin que se ataquen entre ellas. Cmo plantearas la solucin del problema de las NReinas? Piensa en ello.

You might also like