Tema 7 - Algoritmo de Retroceso

23
Laboratorio de Algoritmos de Retroceso ALGORITMICA III Docente: Carlos A. Ruiz De La Cruz Melo Correo: [email protected]

Transcript of Tema 7 - Algoritmo de Retroceso

Page 1: Tema 7 - Algoritmo de Retroceso

Laboratorio de Algoritmos de

Retroceso

ALGORITMICA III

Docente: Carlos A. Ruiz De La Cruz Melo Correo: [email protected]

Page 2: Tema 7 - Algoritmo de Retroceso

LAS n REINAS

Un problema clásico que puede ser resuelto con un diseño Vuelta Atrás es el denominado de las ocho reinas y en general, de las n reinas. Disponemos de un tablero de ajedrez de tamaño 8x8, y se trata de colocar en él ocho reinas de manera que no se amenacen según las normas del ajedrez, es decir, que no se encuentren dos reinas ni en la misma fila, ni en la misma columna, ni en la misma diagonal

Page 3: Tema 7 - Algoritmo de Retroceso

Cualquier solución a este problema estará representada por una 8-tupla [x1,x2, x3 , x4 , x5, x6, x7, x8 ] en la que cada xI representa la columna donde la reina de la fila i-ésima es colocada.

LAS n REINAS

R

R

R

R

R

R

R

R

[ 4, 6, 8, 2, 7, 1, 3, 5 ]

Posible solución

Page 4: Tema 7 - Algoritmo de Retroceso

LAS n REINAS

1. Restricciones explícitas. Formadas por reglas que restringen los valores que pueden tomar los elementos xi a un conjunto determinado. En nuestro problema este conjunto es S={1,2,3,4,5,6,7,8}.

RESTRICCIONES Para decidir en cada etapa cuáles son los valores que puede tomar cada uno de los elementos xi hemos de tener en

cuenta lo que hemos denominado restricciones a fin de que el número de opciones en cada etapa sea el menor posible. En los algoritmos Vuelta Atrás podemos diferenciar dos tipos de restricciones:

Page 5: Tema 7 - Algoritmo de Retroceso

LAS n REINAS

RESTRICCIONES 2. Restricciones implícitas. Indican la relación existente entre los

posibles valores de los xi para que éstos puedan formar parte

de una n-tupla solución. En el problema que nos ocupa podemos definir dos restricciones implícitas.

En primer lugar sabemos que dos reinas no pueden situarse en la

misma columna y por tanto no puede haber dos xi iguales (obsérvese además que la propia definición de la tupla impide situar a dos reinas en la misma fila, con lo cual tenemos cubiertos los dos casos, el de las filas y el de las columnas).

Por otro lado sabemos que dos reinas no pueden estar en la misma diagonal, lo cual reduce el número de opciones. Esta condición se refleja en la segunda restricción implicita que, en forma de ecuación, puede ser expresada como |x – x’| ≠ |y – y’|, siendo (x,y) y (x’,y’) las coordenadas de dos reinas en el tablero.

Page 6: Tema 7 - Algoritmo de Retroceso

[-, -, -, -] 0

[1, -, -, -]1

[1, 2, -, -]2

[1, 3, -, -]3

[1, 4, -, -]6

[2, 1, -, -]11

[2, 3, -, -]12

[2, 4, -, -]13

[1, 3, 2, -]4

[1, 3, 4, -]5

[1, 4, 2, -]7

[1, 4, 3, -]9

[ 2,4,1, -]13

[1, 4, 2,3]8

[2, 4, 1, 3]15

[2, -, -, -]10

ARBOL DE EXPANSION

Page 7: Tema 7 - Algoritmo de Retroceso

ARBOL DE EXPANSION

Configuración final

8 REINAS

Page 8: Tema 7 - Algoritmo de Retroceso

EL RECORRIDO DEL

CABALLO

Se dispone de un tablero rectangular, por ejemplo el tablero de ajedrez, y de un caballo, que se mueve según las reglas de este juego.

El objetivo es encontrar una manera de recorrer todo el tablero partiendo de una casilla determinada, de tal forma que el caballo pase una sola vez por cada casilla.

Una variante es obligar al caballo a volver a la posición de partida en el último movimiento.

Por último se estudiará como encontrar todas las soluciones posibles partiendo de una misma casilla.

Page 9: Tema 7 - Algoritmo de Retroceso

Para resolver el problema hay que realizar todos los movimientos posibles hasta que ya no se pueda avanzar, en cuyo caso hay que dar marcha atrás, o bien hasta que se cubra el tablero.

Además, es necesario determinar la organización de los datos para implementar el algoritmo.

EL RECORRIDO DEL

CABALLO

Page 10: Tema 7 - Algoritmo de Retroceso

EL RECORRIDO DEL

CABALLO

Con las coordenadas en las que se encuentre el caballo y las ocho coordenadas relativas se determina el siguiente movimiento. Las coordenas relativas se guardan en dos arrays:

ejex = [2, 1, -1, -2, -2, -1, 1, 2]

ejey = [1, 2, 2, 1, -1, -2, -2, -1]

Page 11: Tema 7 - Algoritmo de Retroceso

Cuando se encuentra una solución, una variable que se pasa por referencia es puesta a 1 (cierto).

Para codificar el programa, es necesario considerar algunos aspectos más, entre otras cosas no salirse de los límites del tablero y no pisar una casilla ya cubierta (selección del candidato).

Se determina que hay solución cuando ya no hay más casillas que recorrer.

EL RECORRIDO DEL

CABALLO

1 60 39 34 31 18 9 64 38 35 32 61 10 63 30 17

59 2 37 40 33 28 19 8

36 49 42 27 62 11 16 29

43 58 3 50 41 24 7 20

48 51 46 55 26 21 12 15

57 44 53 4 23 14 25 6

52 47 56 45 54 5 22 13

Page 12: Tema 7 - Algoritmo de Retroceso

A continuación se expone un código completo en C, que recubre un tablero cuadrado de lado N partiendo de la posición (0,0).

EL RECORRIDO DEL

CABALLO

#include<stdio.h> #include "conio.h" #define N 8 #define ncuad N*N void mover(int tablero[][N],int i,int pos_x,int pos_y,int *q); int ejex[8]={2,1,-1,-2,-2,-1,1,2}; int ejey[8]={1,2, 2, 1,-1,-2,-2,-1};

Page 13: Tema 7 - Algoritmo de Retroceso

EL RECORRIDO DEL CABALLO

int main(void){

int tablero[N][N];/*tablero de ajedrez*/

int i,j,q;

/*inicializa el tablero a cero*/

for(i=0;i<N;i++)

for(j=0;j<N;j++)

tablero[i][j]=0;

/*pone el primer movimiento*/

tablero[0][0]=1;

mover(tablero,2,0,0,&q);

if(q) {

for(i=0;i<N;i++){

for(j=0;j<N;j++)

printf("%3d ",tablero[i][j]);

putchar('\n');

}

}

else printf("\nno existe solucion\n");

getch();

}

Page 14: Tema 7 - Algoritmo de Retroceso

EL RECORRIDO DEL CABALLO

void mover(int tablero[][N],int i,int pos_x,int pos_y,int *q){

int k,u,v;

k=0;

*q=0;

do{

u=pos_x+ejex[k];

v=pos_y+ejey[k];/*seleccionar candidato*/

if(u>=0 && u<N && v>=0 && v<N){ /*esta dentro de los limites*/

if(tablero[u][v]==0){/*¿es valido?*/

tablero[u][v]=i;

if(i<ncuad) { /*llega al final del recorrido*/

mover(tablero,i+1,u,v,q);

if(!*q) tablero[u][v]=0;

}

else *q=1;/*hay solucion*/

}

}

k++;

}while(!*q && k<8);

} CABALLO

Page 15: Tema 7 - Algoritmo de Retroceso

Definición del problema Conceptualización: El problema se ubica en un contexto en

el que se debe buscar una solución a un laberinto con una entrada y una salida, y con movimientos válidos en cuatro direcciones (no diagonales). En el caso de este ejemplo, el contexto está limitado a una aplicación, en la que el laberinto está representado mediante una matriz de números enteros.

Objetivo: Partiendo de la entrada del laberinto, señalar una ruta que nos guíe hasta la salida.

EL RECORRIDO DEL LABERINTO

Page 16: Tema 7 - Algoritmo de Retroceso

Por ejemplo, dado el siguiente laberinto:

Una solución que nos permitiría alcanzar nuestro objetivo sería la ruta mostrada con asteriscos en la siguiente figura:

EL RECORRIDO DEL LABERINTO

Page 17: Tema 7 - Algoritmo de Retroceso

La solución se basa en una tarea o función que se llamará recursivamente para intentar solucionar el laberinto a partir de una posición particular.

Desde cualquier lugar dentro del laberinto se intentará resolverlo primero intentando hacia izquierda, luego a la derecha, luego arriba y finalmente abajo. Para hacer estos intentos se empleará la función recursiva.

El procedimiento parte de la posición de entrada. Se utilizarán marcas que serán dejadas en el camino para no intentar por caminos por los que ya se pasó y, en particular, no volver atrás salvo si se determina que no hay solución por el camino que se sigue hasta ahora.

Descripción general de la solución

EL RECORRIDO DEL LABERINTO

Page 18: Tema 7 - Algoritmo de Retroceso

La posición de la entrada en el laberinto estará dada por las coordenadas dentro de la matriz, representadas por las variables x0, y0, para la fila y la columna, respectivamente. De manera similar, la posición de la salida del laberinto se guardará en xf, yf .

Cada posición de la matriz puede estar en uno de tres estados: parte del muro (ladrillo) camino libre no visitado camino libre visitado

EL RECORRIDO DEL LABERINTO

Conceptualización de la solución

Variables:

Page 19: Tema 7 - Algoritmo de Retroceso

int lab[FILAS][COLUMNAS] =

{ { L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L },

{ E, E, E, L, E, L, E, L, E, L, E, E, E, L, E, E, E, E, E, E, L },

{ L, L, E, L, E, E, E, L, E, E, E, L, E, L, E, L, E, E, L, E, L },

{ L, E, E, E, E, L, E, L, L, L, E, L, E, L, E, L, L, E, L, E, L },

{ L, E, L, L, L, L, E, L, E, L, E, L, E, L, L, L, E, E, L, E, L },

{ L, E, E, E, L, E, E, L, E, L, E, L, E, E, E, E, E, L, L, E, L },

{ L, E, L, L, L, L, L, L, E, L, E, L, L, E, L, E, E, E, E, E, L },

{ L, E, E, E, E, E, E, E, E, E, E, E, L, E, L, E, L, L, E, L, L },

{ L, L, L, L, L, L, L, L, L, L, L, E, L, E, L, E, E, L, E, L, L },

{ L, E, E, E, E, E, E, E, L, E, E, E, L, L, L, L, L, L, E, L, L },

{ L, E, L, L, L, L, L, E, E, E, L, L, L, E, L, E, E, E, E, E, L },

{ L, E, E, L, E, E, E, E, L, E, E, L, E, E, E, E, L, L, L, E, E },

{ L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L, L } };

EL RECORRIDO DEL LABERINTO

La inicialización de la matriz que aquí se muestra

corresponde al laberinto presentado en la figura del inicio.

Los caminos están formados de espacios libres (E's).

Observe el contorno del laberinto, formado por L's, y los

puntos de entrada y salida.

Page 20: Tema 7 - Algoritmo de Retroceso

EL RECORRIDO DEL LABERINTO

int valida(int f, int c) { int resultado = TRUE; /* Se supone inicialmente valida */ /* Controla si la posicion esta fuera del laberinto */ if ((f<0) || (f>=FILAS) || (c<0) || (c>=COLUMNAS)) resultado = FALSE; /* Controla si la posicion ya fue visitada o es muro */ if (lab[f][c] == MARCA || lab[f][c] == L) resultado = FALSE; return(resultado); }

Esta función analiza una posición dentro del laberinto, especificada por la fila y columna recibidas como parámetro, y deduce si es una posición válida para recorrer.

Básicamente debe verificar que no esté fuera de los límites del laberinto, que no se trate de una posición en la que hay pared, y que no se trate de una posición previamente visitada.

Descripción:

Page 21: Tema 7 - Algoritmo de Retroceso

EL RECORRIDO DEL LABERINTO

int recorrer(int fil, int col) {

int listo = FALSE; /* Indica si se ha encontrado la salida */

/* Se marca la casilla como visitada */

lab[fil][col] = MARCA;

/* Se controla la condicion de termino de recursividad: */

/* " Llegamos a la salida? " */

if (fil == xf && col == yf) return(TRUE);

if (!listo && valida(fil,col-1)) /* Intento a la izquierda */

listo = recorre(fil,col-1);

if (!listo && valida(fil,col+1)) /* Intento a la derecha */

listo = recorre(fil,col+1);

if (!listo && valida(fil-1,col)) /* Intento hacia arriba */

listo = recorre(fil-1,col);

if (!listo && valida(fil+1,col)) /* Intento hacia abajo */

listo = recorre(fil+1,col);

/* Si no se logro resolver el laberinto desde esta posicion, se */

/* desmarca la casilla pues no sera parte de la solucion. En este */

/* caso se retornara falso lo que provocara que se retroceda. */

if (!listo) lab[fil][col] = E;

/* Se retorna TRUE/FALSE dependiendo de si se encontro solucion */

return(listo);

}

Tarea recursiva que recorre el laberinto a partir de una posición dada por la fila y columna pasadas como parámetro y devuelve un valor booleano que indica si se logro llegar a la salida.

Descripción:

Page 22: Tema 7 - Algoritmo de Retroceso

El caso base de la recursividad (condición de termino) es que las coordenadas correspondan con la salida del laberinto.

En caso contrario, se procede a marcar la casilla y a intentar en las distintas direcciones (izquierda, derecha, arriba y abajo), asegurándose primero que en esa dirección haya una casilla valida (no ladrillo, no fuera del laberinto y no visitada).

En caso en que ninguna dirección nos lleva a la salida, se desmarca la casilla actual (pues se va a retroceder) y se devuelve falso como valor de retorno. Las marcas sirven también para señalar la ruta que conforma la solución, una vez alcanzada.

int recorrer(int fil, int col) {

int listo = FALSE; /* Indica si se ha encontrado la salida */

/* Se marca la casilla como visitada */

lab[fil][col] = MARCA;

/* Se controla la condicion de termino de recursividad: */

/* " Llegamos a la salida? " */

if (fil == xf && col == yf) return(TRUE);

if (!listo && valida(fil,col-1)) /* Intento a la izquierda */

listo = recorre(fil,col-1);

if (!listo && valida(fil,col+1)) /* Intento a la derecha */

listo = recorre(fil,col+1);

if (!listo && valida(fil-1,col)) /* Intento hacia arriba */

listo = recorre(fil-1,col);

if (!listo && valida(fil+1,col)) /* Intento hacia abajo */

listo = recorre(fil+1,col);

/* Si no se logro resolver el laberinto desde esta posicion, se */

/* desmarca la casilla pues no sera parte de la solucion. En este */

/* caso se retornara falso lo que provocara que se retroceda. */

if (!listo) lab[fil][col] = E;

/* Se retorna TRUE/FALSE dependiendo de si se encontro solucion */

return(listo);

}

EL RECORRIDO DEL LABERINTO

LABERINTO

Page 23: Tema 7 - Algoritmo de Retroceso

EL RECORRIDO DEL REY

Dado un tablero de ajedrez de tamaño nxn, un rey es colocado en una casilla arbitraria de coordenadas (x,y). El problema consiste en determinar los n 2–1 movimientos de la figura de forma que todas las casillas del tablero sean visitadas una sola vez, si tal secuencia de movimientos existe.