La descarga está en progreso. Por favor, espere

La descarga está en progreso. Por favor, espere

3.  Recursividad.

Presentaciones similares


Presentación del tema: "3.  Recursividad."— Transcripción de la presentación:

1 3.  Recursividad

2 ¿Qué es y para qué se usa? Al programar en forma recursiva, buscamos dentro de un problema otro sub-problema que posea su misma estructura Ejemplo: Calcular xn. // Version 2, estrategia xn = xn/2 * xn/2 public static float elevar( float x, int n )  {     if( n==0 )         return 1;     else if( n es impar )         return x * elevar( x, n-1 );     else         return elevar( x*x, n/2 );  } // Version 1, estrategia: xn = x * xn-1 public static float elevar( float x, int n )  {     if( n==0 )         return 1;     else         return x * elevar(x, n-1);   }

3 Ejemplo 2: Las torres de Hanoi
Pasar las argollas desde la estaca 1 a la 3 Restricciones: Mover una argolla a la vez Nunca puede quedar una argolla mas grande sobre una más pequeña public class TorresDeHanoi { static void Hanoi( int n, int a, int b, int c ) { if( n>0 ) { Hanoi( n-1, a, c, b ); System.out.println( a + " --> " + c ); Hanoi( n-1, b, a, c ); }   public static void main( String[] args ) { Hanoi( Integer.parseInt(args[0]), 1, 2, 3 );

4 Σ 2i = 2n - 1 Breve análisis Se puede demostrar por inducción
Cada invocación del método Hanoi genera a su vez dos llamadas recusrivas Cada llamada recursiva se hace “achicando” el problema en una argolla Cada ejecución toma tiempo constante T(n) = 1 + 2T(n-1) En cada nivel se tienen 21 ejecuciones Σ 2i = 2n - 1 i = 0..n-1 Se puede demostrar por inducción 20 T(n) 21 T(n-1) T(n-1) 22 T(n-2) T(n-2) T(n-2) T(n-2) N veces 2n-1 T(1) T(1) T(1) T(1) T(1) . . .

5 Ejemplo 3: Generar permutaciones
Se tiene un arreglo a[0] . . a[n-1] Se quieren generar (e imprimir) todas las permutaciones posibles Estrategia: intercambiar el primer elemento con el i-esimo y generar todas las permutaciones para los n-1 siguientes, i = 0..n-1 Ej 1,2,3 1 2,3 1 3,2 2 1,3 2 3,1 3 2,1 3 1,2

6 El programa public class PermutaArreglo {
static void permutaciones( int[] x, int ini, int fin) { if( ini == fin ) { imprimir(x); return;} for (int i = ini; i<= fin; i++) { intercambiar(x,ini,i); permutaciones(x, ini+1, fin); } public static void imprimir(int[] x) { for(int i = 0; i < x.length; i++) System.out.print(x[i]+" "); System.out.println();   public static void main( String[] args ) { int[] a = {1,2,3,4,5}; permutaciones( a,0,4 ); public static void intercambiar(int[] x, int y, int z) { int aux = x[y]; x[y] = x[z]; x[z] = aux; }  }

7 Breve análisis Cada invocación del método permutaciones genera a su vez n-1 llamadas recusrivas Cada llamada recursiva se hace “achicando” el problema en un elemento Cada ejecución toma orden n (por el for) T(n) = n + nT(n-1) En cada nivel se tienen n(n-1)(n-2)…(n-i+1) ejecuciones, cada una efectúa k(n-i) instrucciones ( En el último nivel tenemos la n ejecuciones cada una con un elemento n n(n-1) n(n-1)(n-2) n! Cota superior si en todos los niveles colocamos n! y tenemos n niveles tendriamos aprox (n+1)! El resultado está entre n! y (n+1)! (IGUAL MUCHO)

8 El backtraking Solucionar un problema por prueba y error
Se basa en generar todas las posibles soluciones a un problema y probarlas Por esto mismo, el tiempo requerido para solucionar el problema puede explotar Ejemplos típicos: las n-reinas, el caballo, el laberinto

9 Ejemplo 1: el laberinto Se tiene una matriz de caracteres de dimensiones MxN que representa un laberinto. Carácter ‘*’ significa pared, no se puede pasar Carácter ‘ ‘ implica se puede pasar. Carácter ‘&’ indica salida del laberinto public static boolean salida(char[][] x, int i, int j) retorna true si a desde la posición i,j se puede encontrar una salida. * * * * * * * * * *   *       *   * *   * * * & *       *   *   * * * *   *   *   * *   *           *

10 Algoritmo backtraking
Recursivamente esto se puede programar de la siguiente manera probando todos los caminos posibles: si en la posición donde estoy (i,j) hay un ‘*’ no hay salida y retorno false. si en la posición donde estoy (i,j) hay un ‘&‘ entonces estoy fuera y retorno true. si estoy en posición (i,j) y hay un espacio, pruebo recursivamente si hay salida por alguna de las 4 vecinas (i+1,j), (i-1,j), (i,j+1), (i,j-1). si alguna de las llamadas retorna true, yo retorno true (suponemos que no se puede mover en diagonal). Si todas retornan false, retorno false.

11 Prgrama: version 1 public static salida1(char[][] x,i,j) {
if (x[i][j] == ‘&’) return true; if (salida1(x, i+1, j)) return true; if (salida1(x, i-1, j )) return true; if (salida1(x, i, j+1)) return true; if (salida1(x, i, j-1,)) return true; return false; } Esta solución tiene el problema que puede generar llamadas infinitas. Por ejemplo, si llamamos a salida(x, a, b, M,N) y esá vacía pero no es salida, esta llamará a salida(x,a+1,b,M,N). Si la celda (a+1,b) está vacía y no es salida, llamará a salida(x, a+1-1,b,M,N), generandose así un ciclo infinito.

12 Programa version 2 Para evitar esto podemos ir “marcando” (por ejemplo, con una x) los lugares por donde hemos pasado para no pasar de nuevo por ahí: public static boolean salida1( char[][] x, i, j) { if (x[i][j] == ‘&’) return true; if (x[i][j] == '*' || x[i][j] == '+') return false; x[i][j] = ‘o'; if (salida1(x, i+1, j)) return true; if (salida1(x, i-1, j)) return true; if (salida1(x, i, j+1)) return true; if (salida1(x, i, j-1)) return true; return false; }

13 Rescatando el camino Podemos retornar un string que contenga la secuencia de (i,j) por donde hay que pasar para llegar a la salida. Para eso debemos modificar el encabezado public static String sailda(char[][] x, int i, int j) { if (x[i][j] == ‘&’) return "("+i+","+j+")"; String l = s.nextLine(); if (x[i][j] == '*' || x[i][j] == ‘o') return null; x[i][j] = '+'; String camino = (salida2(x, i+1, j)); if (camino != null) return "("+i+","+j+")"+camino; camino = (salida2(x, i-1, j)); camino = (salida2(x, i, j+1)); camino = (salida2(x, i, j-1)); return null; }

14 Camino mas corto Queremos saber cuánto mide el camino (de existir) entre la celda i,j y la salida más próxima. Para esto tenemos que probar todas las posibilidades y nos quedamos con la mejor (más corta): public static int sailda(char[][] x, int i, int j) { if (x[i][j] == ‘&’) return 0; String l = s.nextLine(); if (x[i][j] == '*' || x[i][j] == ‘o') return -1; int mascorto = -1; x[i][j] = '+'; int camino = (salida3(x, i+1, j)); if (camino != -1 && camino < mascorto) mascorto = camino; camino = (salida3(x, i-1, j)); camino = (salida3(x, i, j+1)); camino = (salida3(x, i, j-1)); x[i][j] = ' '; if (mascorto == -1) return -1; return mascorto +1; }

15 Ejemplo: mejor jugada del gato
función que evalúa qué tan buena es una jugada en el gato. suponiendo que tanto mi contrincante como yo vamos a seguir escogiendo la mejor jugada posible en cada etapa. retorno 1 si gano con la jugada x,y, 0 si empato, -1 si pierdo int gato(char[][] t, int x, int y, char z) {    t[x][y] = z;    if (gano(t, z)) return 1;    if (empate(t,x,y,z)) return 0;    char contrincante = 'O';    if (z == 'O') contrincante = 'X';    int mejorCont = -1;    for (int i = 0; i <= 2; i++)     for (int j = 0; j <= 2; j++)        if (t[i][j] == ' ') {          int c = gato(t,i,j,contrincante);          if (c > mejorCont)             mejorCont = c;        }     return -mejorCont: }


Descargar ppt "3.  Recursividad."

Presentaciones similares


Anuncios Google