Professional Documents
Culture Documents
El backtracking es un algoritmo que utiliza una estrategia de En algunas ocasiones el árbol que genera el problema es muy
búsqueda en profundidad y busca la mejor combinación de las grande y encontrar una solución o la solución óptima es
variables para solucionar el problema. Durante la búsqueda, si se computacionalmente muy costoso. En estos casos se suele podar
encuentra una alternativa incorrecta, la búsqueda retrocede hasta el árbol, es decir no se recorrerán ciertos nodos porque debido a
el paso anterior y toma la siguiente alternativa. Cuando se han ciertas características en los mismos ya sabemos que no se
terminado las posibilidades, se vuelve a la elección anterior y se obtendrá una mejor solución a la que ya se tiene o que
toma la siguiente opción. Si no hay más alternativas la búsqueda simplemente no se obtendrá solución.
falla. De esta manera, se crea un árbol implícito, en el que cada
nodo es un estado de la solución (solución parcial en el caso de Este tipo de algoritmos pueden encontrar una o todas las
nodos interiores o solución total en el caso de los nodos hoja). soluciones, siguiendo los siguientes esquemas:
Hemos implementado el algoritmo de forma recursiva, a cada 4.1 Esquema para una solución
llamada le asignamos una dirección posible en la que procedimiento ensayar (paso)
desplazarse a una nueva posición de la matriz donde se volverá a repetir
evaluar los posibles movimientos, si llega un momento que no |seleccionar candidato
puede acceder a ninguna posición no visitada ya desde el lugar |if aceptable then
actual, se considera q se llego a un punto muerto y se vuelve al |begin
nodo anterior donde se moverá a la siguiente posición posible. | anotar_candidato
| if solucion_incompleta then
De este modo el algoritmo ira recorriendo todo el espacio del | begin
laberinto hasta encontrar una salida, y una vez encontrada | ensayar (paso_siguiente)
volverá hacia atrás para ver si es posible encontrar otro camino | if no_acertado then borrar_candidato
con un menor numero de pasos, con lo que no es necesario | end
| else begin
| anotar_solucion
| acertado Åcierto
| end
hasta que (acertado = cierto ) o (candidatos agotados)
fin procedimiento
A continuación mostramos un tablero con dicha pieza colocada Figura 3.posición de las ocho reinas.
en el centro, y las posibles posiciones a las que podrá acceder
partiendo de la posición inicial (Figura 1)
4.3.3 El Problema del Sudoku
Consiste en rellenar un cuadrado de 9x9 formado a su vez por 9
cuadrados de 3x3. En cada cuadrado pequeño se debe colocar un
número del 1 al 9, pero sin que se repita en filas o en columnas.
Figura 4.Sudoku
Y aplicando el algoritmo se obtiene:
5.2 Algoritmo
La implementación se ha llevado a cabo mediante llamadas
recursivas partiendo de la casilla origen. El diagrama de flujo
correspondiente se muestra en la Figura 8. El método se
inicializa sobre la posición de la casilla origen y un paso de 1. A
partir de ahí se mira si dicha casilla es la final, en caso
afirmativo se introduce la posición en el camino solución que se
devuelve a la función llamante. En caso negativo, se comprueba
que no se pueda llegar a dicha casilla por un camino más corto
(menor número de pasos).
En dicho caso se devuelve null al método llamante pues éste
camino no es una posibilidad viable; de no ser así, se actualiza la
Figura 6.posición inicial distancia de ésta casilla al origen, se obtienen los movimientos
posibles desde dicha casilla y se llama al método de forma
Y para obtener la solución del problema se debe llegar a este recurrente para cada uno de ellos.
estado: Se comparan las distancias relativas hasta la meta para cada una
de las posibilidades; se añade la casilla actual al camino de la
mejor de las soluciones y se devuelve dicho camino al método
llamante.
Debido a la naturaleza basada en eventos intrínseca a las
interfaces gráficas y al carácter secuencial de la definición de un
laberinto (definición del mapa, de los puntos de salida y
llegada,…) hemos optados por un diseño basado en estados.
5.4 Codificación
Como Lenguaje de programación escogimos Java por la amplia
experiencia que tenemos con dicho entorno. El programa se
compone de cuatro clases:
• Posicion - representa un punto en coordenadas
cartesianas.
• Casilla - La clase Casilla representa una casilla en el
mapa del laberinto a resolver. Se compone
básicamente de un elemento Posicion que define su
situación en el mapa, un entero para almacenar el
numero de pasos mínimos necesarios para alcanzar
dicha Posicion desde la Casilla de salida y un
booleano indicador de si dicha Casilla forma parte de
la solución. Al ser parte fundamental de la GUI,
Figura 9. Resolución de un laberinto de 20x10x2
Casilla es un tipo Canvas extendido. Los colores * Metodo que implementa de forma recursiva el algoritmo de
backtracking.
mostrados por cada casilla significan: * p - es la posicion de la Casilla a analizar
- gris - muro * paso - el numero de saltos necesarios para alcanzar dicha Casilla por
- blanco - pasillo el camino analizado
- azul - casilla de salida */
public Vector<Posicion> camino(Posicion p,int paso){
- verde - casilla de llegada Vector<Posicion> miCamino=new Vector<Posicion>(1,1);
- rojo - casilla perteneciente al camino solución
• BTAlgorithm – implementa el algoritmo Backtracking //Comprueba que no se haya encontrado el final
if(mapa[p.getX()][p.getY()][p.getZ()].isNotTheEnd()){
óptimo utilizado para resolver el laberinto. //Comprueba que no se pueda llegar a esta Casilla con un
• BackTracker – ésta clase compone la parte gráfica de numero menor de pasos
la aplicación. if((mapa[p.getX()][p.getY()][p.getZ()].getStep())>=(paso)){
//parametros utilizados para encontrar el camino mas corto de
enter todos los posibles desde esta casilla
5.5 Código Fuente int mejordistancia=Integer.MAX_VALUE;
Vector<Posicion> suMejorCamino=null;
5.5.1 BTAlgorithm Vector<Posicion> suCamino;
package backtracker; Object movs[];
import java.util.Vector; //se actualiza el valor de paso de la casilla actual
mapa[p.getX()][p.getY()][p.getZ()].setStep(paso);
/*
* Esta case implementa el algoritmo de BackTracking optimo //se buscan los posibles movimientos se puedan hacer desde
*/ aqui
public class BTAlgorithm{ movs=movPosibles(p);
//dimensiones del mapa
private int dim_x; //para cada posible camino
private int dim_y; for(int i=0;i<movs.length;i++){
private int dim_z; //calcular la solucion
//sucesion de casillas que conforman la solucion suCamino=camino((Posicion)movs[i],paso+1);
Vector movP; //si se encontro una solucion, comparar el numero de pasos
//mapa del laberinto necesarios para esta solucion con las otras posibilidades
Casilla mapa [][][]; if( (suCamino!=null) && (mejordistancia>suCamino.size())
){
public BTAlgorithm(Casilla mapa[][][]) { //por ahora es el mejor camino, actualizar mejor
this.mapa=mapa; distancia
dim_x=mapa.length; suMejorCamino=suCamino;
dim_y=mapa[0].length; mejordistancia=suCamino.size();
dim_z=mapa[0][0].length; }
movP=new Vector(); }
} //si no se encontro ninguna solucion
if(suMejorCamino==null)
/* return null;
* Este etodo analiza las Casilla circundantes y devuelve un array con //si se encontro, añadir esta casilla a la solucion
las posiciones donde hay pasillo miCamino=suMejorCamino;
*/ miCamino.add(p);
Object[] movPosibles(Posicion p){ }
Vector<Posicion> m=new Vector<Posicion>(3,1); else{
return null;
if(p.getY()>0 && mapa[p.getX()][(p.getY()-1)][p.getZ()].isNotWall()){ }
m.addElement(new Posicion(p.getX(),(p.getY()-1),p.getZ())); }
} else{
if(p.getY()<(dim_y-1) && miCamino.addElement(p);
mapa[p.getX()][(p.getY()+1)][p.getZ()].isNotWall()){ }
m.addElement(new Posicion(p.getX(),(p.getY()+1),p.getZ())); return miCamino;
} }
}
if(p.getX()>0 && mapa[(p.getX()-1)][p.getY()][p.getZ()].isNotWall()){
m.addElement(new Posicion((p.getX()-1),p.getY(),p.getZ())); 5.5.2 Posicion
} package backtracker;
if(p.getX()<(dim_x-1) &&
mapa[(p.getX()+1)][p.getY()][p.getZ()].isNotWall()){ /*
m.addElement(new Posicion((p.getX()+1),p.getY(),p.getZ())); * La clase Posicion representa un punto en coordenadas cartesianas
} */
public class Posicion {
if(p.getZ()>0 && mapa[p.getX()][(p.getY())][p.getZ()-1].isNotWall()){
m.addElement(new Posicion(p.getX(),(p.getY()),p.getZ()-1)); private int x;
} private int y;
if(p.getZ()<(dim_z-1) && private int z;
mapa[p.getX()][(p.getY())][p.getZ()+1].isNotWall()){
m.addElement(new Posicion(p.getX(),(p.getY()),p.getZ()+1)); public Posicion(int x,int y,int z) {
} this.x=x;
m.trimToSize(); this.y=y;
return m.toArray(); this.z=z;
} }
public void setX(int nx){
/* x=nx;
}
public int getX(){ setBackground(Color.WHITE);
return x; }
} }
public void setY(int ny){ public void mark(){
y=ny; if(isNotTheEnd() & isNotTheBeginning() & isNotWall()){
} setBackground(Color.RED);
public int getY(){ marked=true;
return y; }
} }
public void setZ(int nz){ public void unmark(){
z=nz; if(marked)
} setBackground(Color.WHITE);
public int getZ(){ }
return z; public boolean isWall(){
} return step == -1;
public String toString(){ }
return "x=" + String.valueOf(x) + " y=" + String.valueOf(y) + " z=" + public boolean isNotWall(){
String.valueOf(z); return step != -1;
} }
} public boolean isTheEnd(){
return step==0;
5.5.3 Casilla }
package backtracker; public boolean isNotTheEnd(){
return step!=0;
import java.awt.Color; }
import java.awt.*; public void setAsEnd(){
step=0;
/* setBackground(Color.GREEN);
* La clase Casilla representa una casilla en el mapa del laberinto a }
resolver. public void unsetAsEnd(){
* Se compone básicamente de un elemento Posicion que define su step=Integer.MAX_VALUE;
situación en el mapa, setBackground(Color.WHITE);
* un entero para almacenar el numero de pasos minimos necesarios para }
alcanzar dicha public boolean isTheBeginning(){
* posicion desde la Casilla de salida y un booleano indicador de si dicha return step==1;
Casilla }
* forma parte de la solución. public boolean isNotTheBeginning(){
* Al ser parte fundamental de la GUI, Casilla es un tipo Canvas extendido. return step!=1;
*/ }
public class Casilla extends Canvas{ public void setAsBeginning(){
//elemento Posicion que define su situación en el mapa. Valores step=1;
especiales son: setBackground(Color.BLUE);
private Posicion pos; }
/* public void unsetAsBeginning(){
* numero de pasos minimos necesarios para alcanzar dicha posicion step=Integer.MAX_VALUE;
desde la Casilla de salida setBackground(Color.WHITE);
* -1 si es representa un elemento no navegable (muro) }
* 0 si representa la casilla de llegada public void setStep(int i){
* 1 si representa la casilla de salida step=i;
*/ }
private int step; public int getStep(){
//indicador de si dicha Casilla forma parte de la solución return step;
private boolean marked; }
public void setPosicion(Posicion p){
public Casilla(int x, int y, int z){ pos=p;
setBackground(Color.LIGHT_GRAY); }
pos=new Posicion(x,y,z); public Posicion getPosicion(){
step=-1; return pos;
marked=false; }
} public void setX(int nx){
pos.setX(nx);
public String toString(){ }
return pos.toString() + " step= " + String.valueOf(step) + " marked= " public void setY(int ny){
+ String.valueOf(marked); pos.setY(ny);
} }
//reinicia la casilla a su valor previo al calculo de la solución public void setZ(int nz){
public void reset(){ pos.setZ(nz);
if(isNotWall()){ }
unmark();
step=Integer.MAX_VALUE; public int getX(){
} return pos.getX();
} }
//cambia la naturaleza de la Casilla entre muro y pasillo
public void changeState(){ public int getY(){
if(isNotWall()){ return pos.getY();
step=-1; }
setBackground(Color.LIGHT_GRAY);
} public int getZ(){
else{ return pos.getZ();
step=Integer.MAX_VALUE; }
} i=Integer.parseInt(alto.getText());
j=Integer.parseInt(ancho.getText());
5.5.4 BackTracker k=Integer.parseInt(pisos.getText());
package backtracker;
maze=new Casilla[i][j][k];
import java.awt.event.*; mapa=new Panel[k];
import java.awt.*; setSize(k*500,600);
import java.util.Vector;
import java.io.*; for(kk=0; kk<k; kk++){
mapa[kk]=new Panel();
/* mapa[kk].setSize(500,500);
* Esta clase implementa la GUI mapa[kk].setBackground(Color.BLACK);
*/ mapa[kk].setLayout(new GridLayout(i,j,1,1));
public class BackTracker extends java.applet.Applet implements tablero.setLayout(new GridLayout(1,k,5,5));
MouseListener, ActionListener, ItemListener{ tablero.add(mapa[kk]);
private static final long serialVersionUID = 1L;
int state; for(ii=0; ii<i; ii++){
boolean endMarked, beginningMarked; for(jj=0; jj<j; jj++){
Casilla beginningTile, endTile; maze[ii][jj][kk]=new Casilla(ii,jj,kk);
Casilla maze[][][]; maze[ii][jj][kk].addMouseListener(this);
Panel tablero; mapa[kk].add(maze[ii][jj][kk]);
Panel control; }
Panel mapa[]; }
Button reset; mapa[kk].doLayout();
Button general; }
Checkbox ffile; tablero.doLayout();
TextField alto; doLayout();
TextField ancho; }
TextField pisos; private void loadFromFile(){
Label altoL; BufferedReader br;
Label anchoL; FileDialog fd;
Label pisosL; Vector<String> lines=new Vector<String>();
String line;
public void init() { char marcas[][];
state=0; int i,ii, j,jj, k,kk;
endMarked=false;
beginningMarked=false; fd=new FileDialog(new Frame(),"Abrir",FileDialog.LOAD);
setSize(500,600); fd.setVisible(true);
reset=new Button("Resetear"); try {
reset.setName("reset"); br=new BufferedReader(new
reset.setEnabled(false); FileReader(fd.getDirectory().concat(fd.getFile()) ));
reset.addActionListener(this); line=br.readLine();
general=new Button("Siguiente"); while(line!=null){
general.setName("general"); lines.add(line);
general.addActionListener(this); line=br.readLine();
ffile=new Checkbox("File",false); }
ffile.addItemListener(this); br.close();
alto=new TextField(3); } catch (FileNotFoundException e) {
ancho=new TextField(3); e.printStackTrace();
pisos=new TextField(2); }catch (IOException e) {
altoL=new Label("Alto"); e.printStackTrace();
anchoL=new Label("Ancho"); }
pisosL=new Label("Pisos");
marcas=new char[lines.size()][((String)lines.elementAt(0)).length()];
setLayout(new BorderLayout()); for(int merlo=0;merlo<marcas.length;merlo++)
tablero=new Panel(); marcas[merlo]=((String)lines.elementAt(merlo)).toCharArray();
tablero.setBackground(Color.RED);
add(tablero,BorderLayout.CENTER); i=marcas.length;
j=marcas[0].length;
control=new Panel(); k=1;
control.setBackground(Color.BLUE);
add(control,BorderLayout.SOUTH); maze=new Casilla[i][j][k];
mapa=new Panel[k];
control.add(reset); setSize(k*500,600);
control.add(general);
control.add(ffile); for(kk=0; kk<k; kk++){
control.add(altoL); mapa[kk]=new Panel();
control.add(alto); mapa[kk].setSize(500,500);
control.add(anchoL); mapa[kk].setBackground(Color.BLACK);
control.add(ancho); mapa[kk].setLayout(new GridLayout(i,j,1,1));
control.add(pisosL); tablero.setLayout(new GridLayout(1,k,5,5));
control.add(pisos); tablero.add(mapa[kk]);
doLayout();
} for(ii=0; ii<i; ii++){
public String getAppletInfo() { for(jj=0; jj<j; jj++){
return "Title: BackTracker"; maze[ii][jj][kk]=new Casilla(ii,jj,kk);
} if(marcas[ii][jj]!='*')
private void loadWithoutFile(){ maze[ii][jj][kk].changeState();
int i,ii, j,jj, k,kk; maze[ii][jj][kk].addMouseListener(this);
mapa[kk].add(maze[ii][jj][kk]);
} ancho.setText("");
} ancho.setEnabled(true);
mapa[kk].doLayout(); pisos.setText("");
} pisos.setEnabled(true);
tablero.doLayout(); }
doLayout(); repaint();
} }
public void actionPerformed(ActionEvent arg0) { public void mouseClicked(MouseEvent arg0) {
String buttonName=((Button)arg0.getSource()).getName(); Casilla source=(Casilla)arg0.getSource();