Anlisis y Diseo de Algoritmos Backtracking Parte 2
Análisis y Diseño de Algoritmos Backtracking - Parte 2 Cesar Liza Avila website : www. cesarliza. com e-mail : creadores@hotmail. com César Liza Avila www. cesarliza. com
El Problema de la Mochila Supóngase que se tiene n objetos distintos y una mochila. Cada objeto tiene asociado un peso wi y un valor vi La mochila puede llevar un peso que no sobrepase su capacidad de carga W. César Liza Avila www. cesarliza. com
Objetivo • Llenar la mochila de tal forma que se maximice el valor de los objetos transportados en ella. • Hay que tener en cuenta que la suma de los pesos de los objetos seleccionados no debe superar la capacidad máxima W de la mochila. Matemáticamente: Maximizar: Sujeto a la restricción: César Liza Avila www. cesarliza. com
Tipos de Mochila • La mochila fraccionaria (siempre funciona la estrategia voraz) • La mochila entera ( 0/1) (no siempre funciona la estrategia voraz) Ahora resolveremos la mochila binaria (0/1) mediante una búsqueda exhaustiva César Liza Avila www. cesarliza. com
Primero pensemos como será nuestro árbol: En cada nivel podemos decidir si elegir o no el artículo. La cantidad de decisiones que se deben tomar depende de la cantidad de artículos y que no sobrepasemos la capacidad de carga de la mochila. Debemos llenar la mochila con aquellos objetos que maximicen el valor que llevamos. 0 1 0 0 1 1 0 1 0 1 1 0 0 1 0 César Liza Avila 1 1 0 1 www. cesarliza. com
Código #include <iostream> using namespace std; #define MAX 30 void bt(int, int); int genera. Candidatos(int, bool []); int n; int Optimo=-1; int peso[MAX]; int valor[MAX]; bool decision[MAX]; void main(void) { int i, capacidad; cout<<"Capacidad de la mochila: "; cin>>capacidad; cout<<"Cantidad de elementos: "; cin>>n; cout<<"Ingrese pesos: "; for(i=0; i<n; i++) cin>>peso[i]; cout<<"Ingrese valores: "; for(i=0; i<n; i++) cin>>valor[i]; bt( 0, capacidad, 0); cout<<endl<<"Valor Optimo: “ <<Optimo<<endl; } void bt(int k, int cap. No. Usada, int valor. Actual) { bool candidato[MAX]; if(k==n) return; int nro. C = genera. Candidatos( k, cap. No. Usada, candidato ); for(int i=0; i<nro. C; i++) { decision[k] = candidato[i]; // toma cada candidato if (decision[k]) { valor. Actual+= valor[k]; if ( valor. Actual>Optimo ) Optimo=valor. Actual; bt(k+1, cap. No. Usada - peso[k], valor. Actual); } else bt(k+1, cap. No. Usada, valor. Actual); } } int genera. Candidatos( int k, int cap. No. Usada, bool candidato[ ] ) { candidato[0]=false; if ( peso[k]<= cap. No. Usada ) { candidato[1]=true; return 2; } else return 1; } César Liza Avila www. cesarliza. com
¿Puede modificar el algoritmo anterior para mostrar que objetos se tomaron? ¿Puede modificar el algoritmo anterior para el caso cuando se disponen varias unidades de cada objeto? ¿Puede modificar el algoritmo anterior para el caso cuando se disponen infinita cantidad de unidades de cada objeto? César Liza Avila www. cesarliza. com
SUDOKU César Liza Avila www. cesarliza. com
El Sudoku se basa en el trabajo sobre Cuadrados Latinos de Leonard Euler. Un cuadrado latino es una matriz de nxn elementos en la que cada celda contiene uno de los n símbolos de tal modo que cada uno de ellos aparece exactamente una vez en cada columna y en cada fila. 1 2 3 1 3 1 2 a b d c b c a d c d d a El SUDOKU se añade una restricción adicional a los cuadrados latinos los subgrupos de 3 x 3 deben tener los dígitos del 1 al 9. d a c b Se llaman así porque Euler utilizó caracteres latinos para representarlo César Liza Avila www. cesarliza. com
Sudoku sū = número, doku = solo Es un tablero de 9 x 9 compuesto por regiones cuadradas de 3 x 3. Algunas celdas ya tienen números, el objetivo es rellenar las celdas vacías, con un número de tal forma que cada columna, fila y región contengan los números del 1 al 9 sin que se repitan (solo una vez sū = número, doku = solo). Un Sudoku bien planteado debe tener una única solución. César Liza Avila www. cesarliza. com
Sudoku www. maatec. com César Liza Avila www. cesarliza. com
Escriba un programa que resuelva un Sudoku, por Backtracking César Liza Avila www. cesarliza. com
#include <iostream> using namespace std; int sudoku[9][9]= { {6, 0, 4, 0, 5, 0, 2, 0, 8}, {0, 5, 0, 0, 0, 4, 0}, {1, 0, 0, 9}, {5, 6, 8, 0, 9, 0, 4, 3, 2}, {4, 0, 0, 8, 0, 6, 0, 0, 5}, {7, 0, 0, 5, 2, 4, 0, 0, 1}, {0, 0, 5, 0, 8, 0, 9, 0, 0}, {0, 4, 2, 0, 0, 0, 6, 8, 0}, {8, 0, 6, 2, 0, 9, 5, 0, 3} }; void res. Sudoku(int, int); bool busca. Celda. Vacia(int &, int &); bool puedo. Colocar(int, int); void imp. Sudoku(void); void main(void) { res. Sudoku(0, 0); } void res. Sudoku(int x, int y) { bool hay. Vacia=busca. Celda. Vacia(x, y); if ( !hay. Vacia ) imp. Sudoku(); else { for(int nro=1; nro<=9; nro++) if ( puedo. Colocar(x, y, nro) ) { sudoku[x][y]=nro; // lo coloco res. Sudoku(x, y+1); // tomo el sgte sudoku[x][y]=0; // lo quito } } } bool busca. Celda. Vacia(int &x, int &y) { for( ; x<9; x++) { for( ; y<9; y++) if( sudoku[x][y]==0 ) return true; y=0; } return false; } César Liza Avila www. cesarliza. com
604 050 208 050 000 040 100 009 bool puedo. Colocar(int f, int c, int nro) { int i, j; // chequeo fila for(j=0; j<9; j++) if(sudoku[f][j]==nro) return false; // chequeo columna for(i=0; i<9; i++) if(sudoku[i][c]==nro) return false; // chequeo subcuadrado int ini. Col = (c/3) *3; int fin. Col = ini. Col+3; int ini. Fil = (f/3) *3; int fin. Fil = ini. Fil+3; for(i=ini. Fil; i<fin. Fil; i++) for(j=ini. Col; j<fin. Col; j++) if(sudoku[i][j]==nro) return false; return true; } 568 090 432 400 806 005 700 524 001 void imp. Sudoku(void) { int i, j; cout<<"--SOLUCION--"<<endl; for(i=0; i<9; i++) { for(j=0; j<9; j++) { cout<<sudoku[i][j]; if ( j==2 || j==5 ) cout<<" "; } cout<<endl; if ( i==2 || i==5 ) cout<<" "<<endl; } cout<<endl; } César Liza Avila 005 080 900 042 000 680 806 209 503 www. cesarliza. com
El Principio del minimax • Suponga que se tienen 2 jugadores MAXI Rodríguez MINNIE Mouse César Liza Avila www. cesarliza. com
MAXI y MINNIE juegan cada uno un turno uno detrás de otro. Si el juego se encuentra en un estado cualquiera, y le toca jugar a MAXI, él querrá maximizar su posibilidad de ganar, para elegir su movimiento piensa en el mejor movimiento que haría MINNIE y actúa en consecuencia. Turno de: 3 3 -2 10 10 -4 3 8 3 -1 -5 4 1 -1 -2 4 -2 -3 -5 Para asignar valores a los nodos más bajos se usa alguna función de evaluación que nos indique tan bueno es ese estado para MAXI, si es el turno de MINNIE ella tratará de minimizar ese valor, si es el turno de MAXI, él tratará de maximizar ese valor. César Liza Avila www. cesarliza. com
3 en Raya Escriba un programa que permita jugar “ 3 en raya” contra la computadora César Liza Avila www. cesarliza. com
#include <iostream> using namespace std; #define GRANDE 100 #define MAX 1 #define MIN 2 #define NADIE '-' #define JUGADOR 'O' #define COMPUTADOR 'X' void main(void) { int f, c, ganador; imp. Tablero(); do { cout<<"Su Turno: "<<endl; cout<<"Fila? "; cin>>f; cout<<"Columna? "; cin>>c; } while( (f<0 || f>=3 || c<0 || c>=3 ) || t[f][c] != NADIE); t[f][c]= JUGADOR; imp. Tablero(); bt (MAX); cout<<"Computadora: "<<endl; cout<<"Fila: "<<mejor. Fila<<endl; cout<<"Columna: "<<mejor. Col<<endl; t[mejor. Fila][mejor. Col]= COMPUTADOR; imp. Tablero(); ganador =eval(); } while(ganador==2); void imp. Tablero(void); int bt(int ); int eval(void); char t[][3]= { {NADIE, NADIE}, {NADIE, NADIE} }; int mejor. Fila, mejor. Col; if(ganador==-1) cout<<"***Ud. Gana***"l; else if(ganador==1) cout<<"***Computadora gana***“; else cout<<"***Empate!***“; cout<<endl; } César Liza Avila www. cesarliza. com
int bt(int modo) { int i, j, val, mf. Local, mc. Local; int valor= eval(); if( valor!=2 ) return valor; if ( modo==MAX ) valor=-GRANDE; else valor=GRANDE; for (i=0; i<3; i++) for (j=0; j<3; j++) if ( t[i][j]==NADIE ) { if ( modo==MAX ) { t[i][j]=COMPUTADOR; val= bt ( MIN ); if ( valor<val ) { valor=val; mf. Local=i; mc. Local=j; } } else { t[i][j]=JUGADOR; val= bt( MAX ); if( valor>val ) { valor=val; mf. Local=i; mc. Local=j; } } t[i][j]=NADIE; } mejor. Fila=mf. Local; mejor. Col=mc. Local; return valor; } César Liza Avila . . . www. cesarliza. com
int eval(void) { int i, j, ganador=NADIE; void imp. Tablero(void) { int i, j; cout<<endl; for(i=0; i<3; i++) { for(j=0; j<3; j++) cout<<t[i][j]; cout<<endl; } // verifica filas for (i=0; i<3; i++) if( t[i][0]!= NADIE && t[i][0] == t[i][1] && t[i][1] == t[i][2] ) ganador = t[i][0]; // verifica columnas for (j=0; j<3; j++) if( t[0][j]!= NADIE && t[0][j] == t[1][j] && t[1][j] == t[2][j] ) ganador = t[0][j]; // verifica diagonal principal if( t[0][0]!= NADIE && t[0][0] == t[1][1] && t[1][1] == t[2][2] ) ganador = t[0][0]; // verifica diagonal secundaria if( t[0][2]!= NADIE && t[0][2] == t[1][1] && t[1][1] == t[2][0] ) ganador = t[0][2]; // verifica si hay ganador if( ganador==JUGADOR ) return -1; // gana jugador if( ganador==COMPUTADOR ) return 1; // gana pc // mira si hay casillas vacias for (i=0; i<3; i++) for (j=0; j<3; j++) if (t[i][j]==NADIE) return 2; // hay libres, aun nadie gana return 0; // no hay libres, empate } César Liza Avila www. cesarliza. com
César Liza Avila www. cesarliza. com
En el “ 3 en raya” entre humanos, es posible al menos empatar. En nuestra implementación como recorremos todos los nodos, entonces no hay sorpresas, así que la computadora nunca perderá. Puede modificar el programa para que solo recorra hasta una profundidad dada. Cuando llegue a esa profundidad evalúe el tablero según la siguiente función. F(t) = ∑ filas que puede ganar MAXI - ∑ filas que puede ganar MINNIE F(t) = 2 – 3 = -1 César Liza Avila www. cesarliza. com
César Liza Avila www. cesarliza. com
- Slides: 23