SlideShare una empresa de Scribd logo
UNIVERSIDAD FERMÍN TORO
VICE-RECTORADO ACADÉMICO
FACULTAD DE INGENIERÍA
ESCUELA DE COMPUTACIÓN
Backtracking
Alumno: Nelson Rodríguez
C.I: 21.725.009
Historia de backtracking
La más antigua referencia a Backtracking, es la historia del hilo de Ariadna en la mitología
griega. Su padre, el rey Minos de Creta detentaba su tiránico poder sobre su isla y por
conquista sobre Atenas. El tributo que pedía de los atenienses consistía en grupos de jóvenes
que entraban a su laberinto donde habitaba el Mino tauro (mitad hombre, mitad toro) para
encontrar la muerte a manos de éste. El héroe ateniense Teseo se ofreció voluntariamente a
acompañar a un grupo de jóvenes que se iban a sacrificar para salvar a Atenas de ésta cruel
tradición.
Ariadna se enamoró de Teseo y le regaló un ovillo de hilo dorado para que no se perdiera en
el laberinto. La idea era que Teseo desenrollara el ovillo a medida que avanzara por el
laberinto, si llegaba a un callejón sin salida tenía que volver atrás y enrollar el hilo hasta
llegar al punto donde había escogido dicho camino para intentar una ruta alternativa. El
Backtracking funcionó, Teseo asesinó al Mino tauro y escapó del laberinto, y de la isla de
Creta con los jóvenes y con Ariadna.
Formalmente el término "backtrack" fue acuñado por primera vez por el matemático
estadounidense D. H. Lehmer en la década de 1950.
Concepto
Los algoritmos backtracking (algoritmos de vuelta atrás), este método para la resolución de
problemas funciona a prueba y error, lo cual lo hace un algoritmo de mucho costo en
cuanto a tiempo para encontrar la solución, ya que el probara todas las opciones para llegar
a solucionar el problema, pero esto nos asegurara la mejor solución al problema planteado.
Enfoque
Los problemas que deben satisfacer un determinado tipo de restricciones son problemas
completos, donde el orden de los elementos de la solución no importa. Estos problemas
consisten en un conjunto (o lista) de variables a la que a cada una se le debe asignar un
valor sujeto a las restricciones del problema. La técnica va creando todas las posibles
combinaciones de elementos para obtener una solución. Su principal virtud es que en la
mayoría de las implementaciones se puede evitar combinaciones, estableciendo funciones
de acotación (o poda) reduciendo el tiempo de ejecución.
Vuelta atrás está muy relacionado con la búsqueda combinatoria.
Diseño e implementación
Esencialmente, la idea es encontrar la mejor combinación posible en un momento
determinado, por eso, se dice que este tipo de algoritmo es una búsqueda en profundidad.
Durante la búsqueda, si se encuentra una alternativa incorrecta, la búsqueda retrocede hasta
el paso anterior y toma la siguiente alternativa. Cuando se han terminado las posibilidades,
se vuelve a la elección anterior y se toma la siguiente opción (hijo [si nos referimos a un
árbol]). Si no hay más alternativas la búsqueda 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
nodos interiores o solución total en el caso de los nodos hoja).
Normalmente, se suele implementar este tipo de algoritmos como un
procedimiento recursivo. Así, en cada llamada al procedimiento se toma una variable y se
le asignan todos los valores posibles, llamando a su vez al procedimiento para cada uno de
los nuevos estados. La diferencia con la búsqueda en profundidad es que se suelen diseñar
funciones de cota, de forma que no se generen algunos estados si no van a conducir a
ninguna solución, o a una solución peor de la que ya se tiene. De esta forma se ahorra
espacio en memoria y tiempo de ejecución.
Heurísticas
Algunas heurísticas son comúnmente usadas para acelerar el proceso. Como las variables se
pueden procesar en cualquier orden, generalmente es más eficiente intentar ser lo más
restrictivo posible con las primeras (esto es, las primeras con menores valores posibles).
Este proceso poda el árbol de búsqueda antes de que se tome la decisión y se llame a la
subrutina recursiva.
Cuando se elige qué valor se va a asignar, muchas implementaciones hacen un examen
hacia delante (FC, Forward Checking), para ver qué valor restringirá el menor número
posible de valores, de forma que se anticipa en a) preservar una posible solución y b) hace
que la solución encontrada no tenga restricciones destacadas.
Algunas implementaciones muy sofisticadas usan una función de cotas, que examina si es
posible encontrar una solución a partir de una solución parcial. Además, se comprueba si la
solución parcial que falla puede incrementar significativamente la eficiencia del algoritmo.
Por el uso de estas funciones de cota, se debe ser muy minucioso en su implementación de
forma que sean poco costosas computacionalmente hablando, ya que lo más normal es que
se ejecuten en para cada nodo o paso del algoritmo. Cabe destacar, que las cotas eficaces se
crean de forma parecida a las funciones heurísticas, esto es, relajando las restricciones para
conseguir mayor eficiencia.
Con el objetivo de mantener la solución actual con coste mínimo, los algoritmos vuelta
atrás mantienen el coste de la mejor solución en una variable que va variando con cada
nueva mejor solución encontrada. Así, si una solución es peor que la que se acaba de
encontrar, el algoritmo no actualizará la solución. De esta forma, devolverá siempre la
mejor solución que haya encontrado. Y recuerden, la vida es un backtracking
Dos ejemplos de aplicación de Vuelta atrás backtracking
1. Sudoku
El famoso juego del Sudoku consiste en rellenar un cubo de 9 x 9 celdas dispuestas
en 9 subgrupos de 3 x 3 celdas, con números del 1 al 9, atendiendo a la restricción
de que no se debe repetir el mismo número en la misma fila, columna o subgrupo de
9.
Un Sudoku dispone de varias celdas con un valor inicial, de modo que debemos
empezar a resolver el problema a partir de esta solución parcial sin modificar
ninguna de las celdas iniciales.
Estrategia de resolución usando Backtracking
El tablero del Sudoku a resolver viene dado por una matriz “Sol [1..9,1..9] de 0..9”
donde Sol[i, j] representa el valor que toma dicha celda, correspondiéndose el valor
0 con una casilla vacía.
Se utilizará una matriz auxiliar “inicial[1..9, 1..9] de Bool” donde inicial[i, j]
representa una celda con valor inicial que no se puede modificar y se corresponde
con la celda “Sol[i, j]”.
A la hora de ramificar el árbol de exploración, solo lo haremos si la solución parcial
que estamos atendiendo es k-prometedora, esto es, si a partir de dicha solución
parcial podremos seguir construyendo soluciones parciales. Para atender a este
punto, utilizaremos una función auxiliar denominada “es_factible”.
La función “es_factible” comprueba para una celda determinada, que no se repita su
valor en la misma fila, columna o subgrupo de 3x3, atendiendo así a la restricción
que comentábamos en la descripción detallada del problema.
Dado que un Sudoku Puede tener varias soluciones, implementaremos el algoritmo
en consecuencia.
Árbol de exploración
El árbol de exploración generado tendrá las siguientes características:
Altura = m + 1:
Siendo m el número de casillas vacías inicialmente.
Nº de Hijos de cada nodo = 9:
Un hijo por cada posible valor de la celda i j.
2. Problema del caballo
El problema del caballo es un antiguo problema matemático en el que se pide que,
teniendo una cuadrícula de n x n casillas y un caballo de ajedrez colocado en una
posición cualquiera ( x, y ), el caballo pase por todas las casillas y una sola vez.
Muchos matemáticos han buscado una solución matemática a este problema, entre
ellos Leonhard Euler.
Se han encontrado muchas soluciones a este problema y de hecho no se sabe con seguridad
de cuántas maneras diferentes es posible solucionarlo.
Algunas variaciones de este problema han sido estudiadas por los matemáticos, tales como:
 Buscar soluciones cíclicas, en la cual se debe llegar a la misma casilla de la cual se
partió.
 Tableros de diferente número de columnas o diferente número de filas.
 Juegos de dos jugadores basados en la idea.
 Problemas usando ligeras variaciones en la forma de moverse el caballo.
El problema del caballo es una forma del problema más general problema de la ruta
Hamiltoniana en la teoría de grafos.
A la derecha podemos apreciar una de las posibles soluciones en un tablero de ajedrez
convencional de ocho columnas por ocho filas. Abajo, una solución cíclica en que la casilla
de destino es justo la anterior a la de partida.
63 14 37 24 51 26 35 10
22 39 62 13 36 11 50 27
15 64 23 38 25 52 9 34
40 21 16 61 12 33 28 49
17 60 1 44 29 48 53 8
2 41 20 57 6 55 32 47
59 18 43 4 45 30 7 54
42 3 58 19 56 5 46 31
Tres ejemplos de problemas comunes resueltos usando Vuelta Atrás programados en
C o C++
1. Sudoku
Dada una matriz 2D de 9 × 9 parcialmente llena 'cuadrícula [9] [9]', el objetivo es
asignar dígitos (del 1 al 9) a las celdas vacías para que cada fila, columna y
subcuadrícula de tamaño 3 × 3 contenga exactamente una instancia de los dígitos del
1 al 9
.
#include <iostream>
using namespace std;
// N is the size of the 2D matrix N*N
#define N 9
/* A utility function to print grid */
void print(int arr[N][N])
{
for (int i = 0; i < N; i++)
{
for (int j = 0; j < N; j++)
cout << arr[i][j] << " ";
cout << endl;
}
}
// Checks whether it will be
// legal to assign num to the
// given row, col
bool isSafe(int grid[N][N], int row,
int col, int num)
{
// Check if we find the same num
// in the similar row , we
// return false
for (int x = 0; x <= 8; x++)
if (grid[row][x] == num)
return false;
// Check if we find the same num in
// the similar column , we
// return false
for (int x = 0; x <= 8; x++)
if (grid[x][col] == num)
return false;
// Check if we find the same num in
// the particular 3*3 matrix,
// we return false
int startRow = row - row % 3,
startCol = col - col % 3;
for (int i = 0; i < 3; i++)
for (int j = 0; j < 3; j++)
if (grid[i + startRow][j +
startCol] == num)
return false;
return true;
}
/* Takes a partially filled-in grid and attempts
to assign values to all unassigned locations in
such a way to meet the requirements for
Sudoku solution (non-duplication across rows,
columns, and boxes) */
bool solveSuduko(int grid[N][N], int row, int col)
{
// Check if we have reached the 8th
// row and 9th column (0
// indexed matrix) , we are
// returning true to avoid
// further backtracking
if (row == N - 1 && col == N)
return true;
// Check if column value becomes 9 ,
// we move to next row and
// column start from 0
if (col == N) {
row++;
col = 0;
}
// Check if the current position of
// the grid already contains
// value >0, we iterate for next column
if (grid[row][col] > 0)
return solveSuduko(grid, row, col + 1);
for (int num = 1; num <= N; num++)
{
// Check if it is safe to place
// the num (1-9) in the
// given row ,col ->we
// move to next column
if (isSafe(grid, row, col, num))
{
/* Assigning the num in
the current (row,col)
position of the grid
and assuming our assined
num in the position
is correct */
grid[row][col] = num;
// Checking for next possibility with next
// column
if (solveSuduko(grid, row, col + 1))
return true;
}
// Removing the assigned num ,
// since our assumption
// was wrong , and we go for
// next assumption with
// diff num value
grid[row][col] = 0;
}
return false;
}
// Driver Code
int main()
{
// 0 means unassigned cells
int grid[N][N] = { { 3, 0, 6, 5, 0, 8, 4, 0, 0 },
{ 5, 2, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 8, 7, 0, 0, 0, 0, 3, 1 },
{ 0, 0, 3, 0, 1, 0, 0, 8, 0 },
{ 9, 0, 0, 8, 6, 3, 0, 0, 5 },
{ 0, 5, 0, 0, 9, 0, 6, 0, 0 },
{ 1, 3, 0, 0, 0, 0, 2, 5, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 7, 4 },
{ 0, 0, 5, 2, 0, 6, 3, 0, 0 } };
if (solveSuduko(grid, 0, 0))
print(grid);
else
cout << "no solution exists " << endl;
return 0;
}
2. Rata en el laberinto
Analicemos Rata en el laberinto como otro problema de ejemplo que se puede
resolver usando Backtracking.
Un laberinto se da como una matriz binaria de bloques N * N donde el bloque de
origen es el bloque superior izquierdo, es decir, el laberinto [0] [0] y el bloque de
destino es el bloque inferior derecho, es decir, laberinto [N-1] [N-1] . Una rata parte
de la fuente y tiene que llegar al destino. La rata solo puede moverse en dos
direcciones: hacia adelante y hacia abajo.
En la matriz de laberinto, 0 significa que el bloque es un callejón sin salida y 1
significa que el bloque se puede utilizar en la ruta desde el origen hasta el destino.
Tenga en cuenta que esta es una versión simple del típico problema de Maze. Por
ejemplo, una versión más compleja puede ser que la rata se pueda mover en 4
direcciones y una versión más compleja puede ser con un número limitado de
movimientos.
A continuación se muestra un ejemplo de laberinto.
Los bloques grises son callejones sin salida (valor = 0).
A continuación se muestra una representación matricial binaria del laberinto
anterior.
{1, 0, 0, 0}
{1, 1, 0, 1}
{0, 1, 0, 0}
{1, 1, 1, 1}
/* C++ program to solve Rat in
a Maze problem using backtracking */
#include <stdio.h>
// Maze size
#define N 4
bool solveMazeUtil(
int maze[N][N], int x,
int y, int sol[N][N]);
/* A utility function to print
solution matrix sol[N][N] */
void printSolution(int sol[N][N])
{
for (int i = 0; i < N; i++) {
for (int j = 0; j < N; j++)
printf(" %d ", sol[i][j]);
printf("n");
}
}
/* A utility function to check if x,
y is valid index for N*N maze */
bool isSafe(int maze[N][N], int x, int y)
{
// if (x, y outside maze) return false
if (
x >= 0 && x < N && y >= 0
&& y < N && maze[x][y] == 1)
return true;
return false;
}
/* This function solves the Maze problem
using Backtracking. It mainly uses
solveMazeUtil() to solve the problem.
It returns false if no path is possible,
otherwise return true and prints the path
in the form of 1s. Please note that there
may be more than one solutions, this
function prints one of the feasible
solutions.*/
bool solveMaze(int maze[N][N])
{
int sol[N][N] = { { 0, 0, 0, 0 },
{ 0, 0, 0, 0 },
{ 0, 0, 0, 0 },
{ 0, 0, 0, 0 } };
if (solveMazeUtil(
maze, 0, 0, sol)
== false) {
printf("Solution doesn't exist");
return false;
}
printSolution(sol);
return true;
}
/* A recursive utility function
to solve Maze problem */
bool solveMazeUtil(
int maze[N][N], int x,
int y, int sol[N][N])
{
// if (x, y is goal) return true
if (
x == N - 1 && y == N - 1
&& maze[x][y] == 1) {
sol[x][y] = 1;
return true;
}
// Check if maze[x][y] is valid
if (isSafe(maze, x, y) == true) {
// Check if the current block is already part of solution path.
if (sol[x][y] == 1)
return false;
// mark x, y as part of solution path
sol[x][y] = 1;
/* Move forward in x direction */
if (solveMazeUtil(
maze, x + 1, y, sol)
== true)
return true;
/* If moving in x direction
doesn't give solution then
Move down in y direction */
if (solveMazeUtil(
maze, x, y + 1, sol)
== true)
return true;
/* If moving in y direction
doesn't give solution then
Move back in x direction */
if (solveMazeUtil(
maze, x - 1, y, sol)
== true)
return true;
/* If moving backwards in x direction
doesn't give solution then
Move upwards in y direction */
if (solveMazeUtil(
maze, x, y - 1, sol)
== true)
return true;
/* If none of the above movements
work then BACKTRACK: unmark
x, y as part of solution path */
sol[x][y] = 0;
return false;
}
return false;
}
// driver program to test above function
int main()
{
int maze[N][N] = { { 1, 0, 0, 0 },
{ 1, 1, 0, 1 },
{ 0, 1, 0, 0 },
{ 1, 1, 1, 1 } };
solveMaze(maze);
return 0;
}
3. El problema del tour del caballo
Planteamiento del problema:
Dado un tablero N * N con el Caballo colocado en el primer bloque de un tablero
vacío. Moverse de acuerdo con las reglas del caballo de ajedrez debe visitar cada
casilla exactamente una vez. Imprime el orden de cada celda en la que se visitan.
Ejemplo:
Aporte:
N = 8
Producción:
0 59 38 33 30 17 8 63
37 34 31 60 9 62 29 16
58 1 36 39 32 27 18 7
35 48 41 26 61 10 15 28
42 57 2 49 40 23 6 19
47 50 45 54 25 20 11 14
56 43 52 3 22 13 24 5
51 46 55 44 53 4 21 12
El camino que siguió el caballero para cubrir todas las celdas.
A continuación se muestra un tablero de ajedrez con 8 x 8 celdas. Los números en
las celdas indican el número de movimiento del Caballero.
// C++ program for Knight Tour problem
#include <bits/stdc++.h>
using namespace std;
#define N 8
int solveKTUtil(int x, int y, int movei, int sol[N][N],
int xMove[], int yMove[]);
/* A utility function to check if i,j are
valid indexes for N*N chessboard */
int isSafe(int x, int y, int sol[N][N])
{
return (x >= 0 && x < N && y >= 0 && y < N
&& sol[x][y] == -1);
}
/* A utility function to print
solution matrix sol[N][N] */
void printSolution(int sol[N][N])
{
for (int x = 0; x < N; x++) {
for (int y = 0; y < N; y++)
cout << " " << setw(2) << sol[x][y] << " ";
cout << endl;
}
}
/* This function solves the Knight Tour problem using
Backtracking. This function mainly uses solveKTUtil()
to solve the problem. It returns false if no complete
tour is possible, otherwise return true and prints the
tour.
Please note that there may be more than one solutions,
this function prints one of the feasible solutions. */
int solveKT()
{
int sol[N][N];
/* Initialization of solution matrix */
for (int x = 0; x < N; x++)
for (int y = 0; y < N; y++)
sol[x][y] = -1;
/* xMove[] and yMove[] define next move of Knight.
xMove[] is for next value of x coordinate
yMove[] is for next value of y coordinate */
int xMove[8] = { 2, 1, -1, -2, -2, -1, 1, 2 };
int yMove[8] = { 1, 2, 2, 1, -1, -2, -2, -1 };
// Since the Knight is initially at the first block
sol[0][0] = 0;
/* Start from 0,0 and explore all tours using
solveKTUtil() */
if (solveKTUtil(0, 0, 1, sol, xMove, yMove) == 0) {
cout << "Solution does not exist";
return 0;
}
else
printSolution(sol);
return 1;
}
/* A recursive utility function to solve Knight Tour
problem */
int solveKTUtil(int x, int y, int movei, int sol[N][N],
int xMove[N], int yMove[N])
{
int k, next_x, next_y;
if (movei == N * N)
return 1;
/* Try all next moves from
the current coordinate x, y */
for (k = 0; k < 8; k++) {
next_x = x + xMove[k];
next_y = y + yMove[k];
if (isSafe(next_x, next_y, sol)) {
sol[next_x][next_y] = movei;
if (solveKTUtil(next_x, next_y, movei + 1, sol,
xMove, yMove)
== 1)
return 1;
else
// backtracking
sol[next_x][next_y] = -1;
}
}
return 0;
}
// Driver Code
int main()
{
// Function Call
solveKT();
return 0;
}
Vuelta atrás o Backtracking para la enumeración
El problema de la enumeración consiste en encontrar todas las soluciones del problema, es
por ello que tendremos que recorrer el árbol de estados al completo.
Algoritmo de Backtracking para la enumeración:
proc Bactracking Enum(↕X[1 . . . i ]: TSolución, ↑num: N)
variables L: ListaComponentes
inicio
si EsSolución (X) entonces num num+1
EscribeSolución (X)
en otro caso
L Candidatos (X)
mientras ¬Vacía (L) hacer
X[i + 1] Cabeza (L); L Resto (L)
BacktrackingEnum (X, num)
finmientras
finsi
fin
Aplicaciones
Vuelta atrás se usa en la implementación de los lenguajes de programación tales
como Lenguaje de programación Planner y Prolog. Además, se usa en los análisis
sintácticos de los compiladores. Su uso en inteligencia artificial ha sido muy
importante, dando lugar a nuevos tipos de búsquedas como el A estrella.
Branch & Bound (Ramificación y poda).
Este método busca una solución como en el método de backtracking, pero cada solución
tiene asociado un costo y la solución que se busca es una de mínimo costo llamada
óptima. Además de ramificar una solución padre (branch) en hijos se trata de eliminar
de consideración aquellos hijos cuyos descendientes tienen un costo que supera al
óptimo buscado acotando el costo de los descendientes del hijo (bound). La forma de
acotar es un arte que depende de cada problema. La acotación reduce el tiempo de
búsqueda de la solución óptima al "podar" (pruning) los subarboles de descendientes
costosos.

Más contenido relacionado

PPT
Divide y Venceras
PDF
Metodologia de la programacion recursividad
PDF
Fundamentos Divide Y Venceras
PPTX
Investigacion de operaciones 10% segundo corte
DOCX
Divide y Venceras
Metodologia de la programacion recursividad
Fundamentos Divide Y Venceras
Investigacion de operaciones 10% segundo corte

La actualidad más candente (20)

DOCX
Maximos y minimos funcion de varias variables
DOC
Integrales indefinidas
PDF
2 integracion
PDF
Apuntes de calculo integral fracciones parciales (9) pof. luis castro pérez
DOCX
Algoritmos divide y vencerás
PPT
Optimización sin restricciones
PPTX
Calculo diferencial fie.ppt
PDF
Divide y Vencerás
PPTX
Clase 3 derivada
PDF
Matematicas cap5
PPT
Varian04espanol
PDF
Metodos de eliminacion gaussiana
PDF
Fracciones parciales
PPTX
Resumen de la unidad iii (analisis numerico) Mirian Rodriguez
PDF
Cm prct7 0910
PDF
Variables aleatorias bidemensionales
DOCX
Unidad 2 calculo integral
DOC
Metodo de integración POR PARTES.
PDF
Que es complejidad computacional
PDF
Matematica final3
Maximos y minimos funcion de varias variables
Integrales indefinidas
2 integracion
Apuntes de calculo integral fracciones parciales (9) pof. luis castro pérez
Algoritmos divide y vencerás
Optimización sin restricciones
Calculo diferencial fie.ppt
Divide y Vencerás
Clase 3 derivada
Matematicas cap5
Varian04espanol
Metodos de eliminacion gaussiana
Fracciones parciales
Resumen de la unidad iii (analisis numerico) Mirian Rodriguez
Cm prct7 0910
Variables aleatorias bidemensionales
Unidad 2 calculo integral
Metodo de integración POR PARTES.
Que es complejidad computacional
Matematica final3
Publicidad

Similar a Slideshare nelson rodriguez (20)

PPTX
Backtracking (Método de Vuelta Atrás)
PPTX
Daniela mendozaestructuradedatosii
PPTX
Método de Vuelta Atrás (Backtracking)
DOCX
Busqueda por backtracking
PPTX
Técnica del backtracking o vuelta atrás
PDF
Enfoques
PPTX
PPTX
Algoritmo de backtracking
PPT
Estructura de datos.ppt
PDF
Problema de las 8 reinas
PPTX
Backtracking
PPTX
Programación Dinámica
PPTX
Enfoques
PPTX
Backtracking
PPT
3 recursividad
DOCX
Inteligencia Artificial - Leonys B
DOC
Clase5 Ia
DOC
Clase5 Ia
PDF
12 Solucion De Problemas Con Recursion
 
Backtracking (Método de Vuelta Atrás)
Daniela mendozaestructuradedatosii
Método de Vuelta Atrás (Backtracking)
Busqueda por backtracking
Técnica del backtracking o vuelta atrás
Enfoques
Algoritmo de backtracking
Estructura de datos.ppt
Problema de las 8 reinas
Backtracking
Programación Dinámica
Enfoques
Backtracking
3 recursividad
Inteligencia Artificial - Leonys B
Clase5 Ia
Clase5 Ia
12 Solucion De Problemas Con Recursion
 
Publicidad

Último (20)

PDF
TRAUMA_Y_RECUPERACION consecuencias de la violencia JUDITH HERMAN
PDF
La Evaluacion Formativa en Nuevos Escenarios de Aprendizaje UGEL03 Ccesa007.pdf
PDF
Cronograma de clases de Práctica Profesional 2 2025 UDE.pdf
PDF
Punto Critico - Brian Tracy Ccesa007.pdf
PDF
SESION 12 INMUNIZACIONES - CADENA DE FRÍO- SALUD FAMILIAR - PUEBLOS INDIGENAS...
PDF
ciencias-1.pdf libro cuarto basico niños
PDF
CONFERENCIA-Deep Research en el aula universitaria-UPeU-EduTech360.pdf
PDF
el - LIBRO-PACTO-EDUCATIVO-GLOBAL-OIEC.pdf
PDF
1. Intrdoduccion y criterios de seleccion de Farm 2024.pdf
PDF
Tomo 1 de biologia gratis ultra plusenmas
DOCX
UNIDAD DE APRENDIZAJE 5 AGOSTO tradiciones
PPTX
caso clínico iam clinica y semiología l3.pptx
DOCX
PROYECTO DE APRENDIZAJE para la semana de fiestas patrias
PPTX
Doctrina 1 Soteriologuia y sus diferente
PDF
Híper Mega Repaso Histológico Bloque 3.pdf
PDF
Guia de Tesis y Proyectos de Investigacion FS4 Ccesa007.pdf
DOCX
2 GRADO UNIDAD 5 - 2025.docx para primaria
PDF
Romper el Circulo de la Creatividad - Colleen Hoover Ccesa007.pdf
PDF
Unidad de Aprendizaje 5 de Matematica 1ro Secundaria Ccesa007.pdf
DOCX
V UNIDAD - SEGUNDO GRADO. del mes de agosto
TRAUMA_Y_RECUPERACION consecuencias de la violencia JUDITH HERMAN
La Evaluacion Formativa en Nuevos Escenarios de Aprendizaje UGEL03 Ccesa007.pdf
Cronograma de clases de Práctica Profesional 2 2025 UDE.pdf
Punto Critico - Brian Tracy Ccesa007.pdf
SESION 12 INMUNIZACIONES - CADENA DE FRÍO- SALUD FAMILIAR - PUEBLOS INDIGENAS...
ciencias-1.pdf libro cuarto basico niños
CONFERENCIA-Deep Research en el aula universitaria-UPeU-EduTech360.pdf
el - LIBRO-PACTO-EDUCATIVO-GLOBAL-OIEC.pdf
1. Intrdoduccion y criterios de seleccion de Farm 2024.pdf
Tomo 1 de biologia gratis ultra plusenmas
UNIDAD DE APRENDIZAJE 5 AGOSTO tradiciones
caso clínico iam clinica y semiología l3.pptx
PROYECTO DE APRENDIZAJE para la semana de fiestas patrias
Doctrina 1 Soteriologuia y sus diferente
Híper Mega Repaso Histológico Bloque 3.pdf
Guia de Tesis y Proyectos de Investigacion FS4 Ccesa007.pdf
2 GRADO UNIDAD 5 - 2025.docx para primaria
Romper el Circulo de la Creatividad - Colleen Hoover Ccesa007.pdf
Unidad de Aprendizaje 5 de Matematica 1ro Secundaria Ccesa007.pdf
V UNIDAD - SEGUNDO GRADO. del mes de agosto

Slideshare nelson rodriguez

  • 1. UNIVERSIDAD FERMÍN TORO VICE-RECTORADO ACADÉMICO FACULTAD DE INGENIERÍA ESCUELA DE COMPUTACIÓN Backtracking Alumno: Nelson Rodríguez C.I: 21.725.009
  • 2. Historia de backtracking La más antigua referencia a Backtracking, es la historia del hilo de Ariadna en la mitología griega. Su padre, el rey Minos de Creta detentaba su tiránico poder sobre su isla y por conquista sobre Atenas. El tributo que pedía de los atenienses consistía en grupos de jóvenes que entraban a su laberinto donde habitaba el Mino tauro (mitad hombre, mitad toro) para encontrar la muerte a manos de éste. El héroe ateniense Teseo se ofreció voluntariamente a acompañar a un grupo de jóvenes que se iban a sacrificar para salvar a Atenas de ésta cruel tradición. Ariadna se enamoró de Teseo y le regaló un ovillo de hilo dorado para que no se perdiera en el laberinto. La idea era que Teseo desenrollara el ovillo a medida que avanzara por el laberinto, si llegaba a un callejón sin salida tenía que volver atrás y enrollar el hilo hasta llegar al punto donde había escogido dicho camino para intentar una ruta alternativa. El Backtracking funcionó, Teseo asesinó al Mino tauro y escapó del laberinto, y de la isla de Creta con los jóvenes y con Ariadna. Formalmente el término "backtrack" fue acuñado por primera vez por el matemático estadounidense D. H. Lehmer en la década de 1950. Concepto Los algoritmos backtracking (algoritmos de vuelta atrás), este método para la resolución de problemas funciona a prueba y error, lo cual lo hace un algoritmo de mucho costo en cuanto a tiempo para encontrar la solución, ya que el probara todas las opciones para llegar a solucionar el problema, pero esto nos asegurara la mejor solución al problema planteado. Enfoque Los problemas que deben satisfacer un determinado tipo de restricciones son problemas completos, donde el orden de los elementos de la solución no importa. Estos problemas consisten en un conjunto (o lista) de variables a la que a cada una se le debe asignar un
  • 3. valor sujeto a las restricciones del problema. La técnica va creando todas las posibles combinaciones de elementos para obtener una solución. Su principal virtud es que en la mayoría de las implementaciones se puede evitar combinaciones, estableciendo funciones de acotación (o poda) reduciendo el tiempo de ejecución. Vuelta atrás está muy relacionado con la búsqueda combinatoria. Diseño e implementación Esencialmente, la idea es encontrar la mejor combinación posible en un momento determinado, por eso, se dice que este tipo de algoritmo es una búsqueda en profundidad. Durante la búsqueda, si se encuentra una alternativa incorrecta, la búsqueda retrocede hasta el paso anterior y toma la siguiente alternativa. Cuando se han terminado las posibilidades, se vuelve a la elección anterior y se toma la siguiente opción (hijo [si nos referimos a un árbol]). Si no hay más alternativas la búsqueda 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 nodos interiores o solución total en el caso de los nodos hoja). Normalmente, se suele implementar este tipo de algoritmos como un procedimiento recursivo. Así, en cada llamada al procedimiento se toma una variable y se le asignan todos los valores posibles, llamando a su vez al procedimiento para cada uno de los nuevos estados. La diferencia con la búsqueda en profundidad es que se suelen diseñar funciones de cota, de forma que no se generen algunos estados si no van a conducir a ninguna solución, o a una solución peor de la que ya se tiene. De esta forma se ahorra espacio en memoria y tiempo de ejecución. Heurísticas Algunas heurísticas son comúnmente usadas para acelerar el proceso. Como las variables se pueden procesar en cualquier orden, generalmente es más eficiente intentar ser lo más restrictivo posible con las primeras (esto es, las primeras con menores valores posibles). Este proceso poda el árbol de búsqueda antes de que se tome la decisión y se llame a la subrutina recursiva. Cuando se elige qué valor se va a asignar, muchas implementaciones hacen un examen hacia delante (FC, Forward Checking), para ver qué valor restringirá el menor número posible de valores, de forma que se anticipa en a) preservar una posible solución y b) hace que la solución encontrada no tenga restricciones destacadas. Algunas implementaciones muy sofisticadas usan una función de cotas, que examina si es posible encontrar una solución a partir de una solución parcial. Además, se comprueba si la solución parcial que falla puede incrementar significativamente la eficiencia del algoritmo. Por el uso de estas funciones de cota, se debe ser muy minucioso en su implementación de forma que sean poco costosas computacionalmente hablando, ya que lo más normal es que se ejecuten en para cada nodo o paso del algoritmo. Cabe destacar, que las cotas eficaces se crean de forma parecida a las funciones heurísticas, esto es, relajando las restricciones para conseguir mayor eficiencia.
  • 4. Con el objetivo de mantener la solución actual con coste mínimo, los algoritmos vuelta atrás mantienen el coste de la mejor solución en una variable que va variando con cada nueva mejor solución encontrada. Así, si una solución es peor que la que se acaba de encontrar, el algoritmo no actualizará la solución. De esta forma, devolverá siempre la mejor solución que haya encontrado. Y recuerden, la vida es un backtracking Dos ejemplos de aplicación de Vuelta atrás backtracking 1. Sudoku El famoso juego del Sudoku consiste en rellenar un cubo de 9 x 9 celdas dispuestas en 9 subgrupos de 3 x 3 celdas, con números del 1 al 9, atendiendo a la restricción de que no se debe repetir el mismo número en la misma fila, columna o subgrupo de 9. Un Sudoku dispone de varias celdas con un valor inicial, de modo que debemos empezar a resolver el problema a partir de esta solución parcial sin modificar ninguna de las celdas iniciales. Estrategia de resolución usando Backtracking El tablero del Sudoku a resolver viene dado por una matriz “Sol [1..9,1..9] de 0..9” donde Sol[i, j] representa el valor que toma dicha celda, correspondiéndose el valor 0 con una casilla vacía. Se utilizará una matriz auxiliar “inicial[1..9, 1..9] de Bool” donde inicial[i, j] representa una celda con valor inicial que no se puede modificar y se corresponde con la celda “Sol[i, j]”. A la hora de ramificar el árbol de exploración, solo lo haremos si la solución parcial que estamos atendiendo es k-prometedora, esto es, si a partir de dicha solución parcial podremos seguir construyendo soluciones parciales. Para atender a este punto, utilizaremos una función auxiliar denominada “es_factible”. La función “es_factible” comprueba para una celda determinada, que no se repita su valor en la misma fila, columna o subgrupo de 3x3, atendiendo así a la restricción que comentábamos en la descripción detallada del problema. Dado que un Sudoku Puede tener varias soluciones, implementaremos el algoritmo en consecuencia. Árbol de exploración El árbol de exploración generado tendrá las siguientes características: Altura = m + 1: Siendo m el número de casillas vacías inicialmente.
  • 5. Nº de Hijos de cada nodo = 9: Un hijo por cada posible valor de la celda i j. 2. Problema del caballo El problema del caballo es un antiguo problema matemático en el que se pide que, teniendo una cuadrícula de n x n casillas y un caballo de ajedrez colocado en una posición cualquiera ( x, y ), el caballo pase por todas las casillas y una sola vez. Muchos matemáticos han buscado una solución matemática a este problema, entre ellos Leonhard Euler. Se han encontrado muchas soluciones a este problema y de hecho no se sabe con seguridad de cuántas maneras diferentes es posible solucionarlo. Algunas variaciones de este problema han sido estudiadas por los matemáticos, tales como:  Buscar soluciones cíclicas, en la cual se debe llegar a la misma casilla de la cual se partió.  Tableros de diferente número de columnas o diferente número de filas.  Juegos de dos jugadores basados en la idea.  Problemas usando ligeras variaciones en la forma de moverse el caballo. El problema del caballo es una forma del problema más general problema de la ruta Hamiltoniana en la teoría de grafos. A la derecha podemos apreciar una de las posibles soluciones en un tablero de ajedrez convencional de ocho columnas por ocho filas. Abajo, una solución cíclica en que la casilla de destino es justo la anterior a la de partida. 63 14 37 24 51 26 35 10 22 39 62 13 36 11 50 27 15 64 23 38 25 52 9 34 40 21 16 61 12 33 28 49 17 60 1 44 29 48 53 8 2 41 20 57 6 55 32 47 59 18 43 4 45 30 7 54 42 3 58 19 56 5 46 31
  • 6. Tres ejemplos de problemas comunes resueltos usando Vuelta Atrás programados en C o C++ 1. Sudoku Dada una matriz 2D de 9 × 9 parcialmente llena 'cuadrícula [9] [9]', el objetivo es asignar dígitos (del 1 al 9) a las celdas vacías para que cada fila, columna y subcuadrícula de tamaño 3 × 3 contenga exactamente una instancia de los dígitos del 1 al 9 . #include <iostream> using namespace std; // N is the size of the 2D matrix N*N #define N 9 /* A utility function to print grid */ void print(int arr[N][N]) { for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) cout << arr[i][j] << " "; cout << endl; } } // Checks whether it will be // legal to assign num to the // given row, col bool isSafe(int grid[N][N], int row, int col, int num)
  • 7. { // Check if we find the same num // in the similar row , we // return false for (int x = 0; x <= 8; x++) if (grid[row][x] == num) return false; // Check if we find the same num in // the similar column , we // return false for (int x = 0; x <= 8; x++) if (grid[x][col] == num) return false; // Check if we find the same num in // the particular 3*3 matrix, // we return false int startRow = row - row % 3, startCol = col - col % 3; for (int i = 0; i < 3; i++) for (int j = 0; j < 3; j++) if (grid[i + startRow][j + startCol] == num) return false; return true; } /* Takes a partially filled-in grid and attempts to assign values to all unassigned locations in such a way to meet the requirements for Sudoku solution (non-duplication across rows, columns, and boxes) */ bool solveSuduko(int grid[N][N], int row, int col) { // Check if we have reached the 8th // row and 9th column (0 // indexed matrix) , we are // returning true to avoid // further backtracking if (row == N - 1 && col == N) return true; // Check if column value becomes 9 ,
  • 8. // we move to next row and // column start from 0 if (col == N) { row++; col = 0; } // Check if the current position of // the grid already contains // value >0, we iterate for next column if (grid[row][col] > 0) return solveSuduko(grid, row, col + 1); for (int num = 1; num <= N; num++) { // Check if it is safe to place // the num (1-9) in the // given row ,col ->we // move to next column if (isSafe(grid, row, col, num)) { /* Assigning the num in the current (row,col) position of the grid and assuming our assined num in the position is correct */ grid[row][col] = num; // Checking for next possibility with next // column if (solveSuduko(grid, row, col + 1)) return true; } // Removing the assigned num , // since our assumption // was wrong , and we go for // next assumption with // diff num value grid[row][col] = 0; } return false; }
  • 9. // Driver Code int main() { // 0 means unassigned cells int grid[N][N] = { { 3, 0, 6, 5, 0, 8, 4, 0, 0 }, { 5, 2, 0, 0, 0, 0, 0, 0, 0 }, { 0, 8, 7, 0, 0, 0, 0, 3, 1 }, { 0, 0, 3, 0, 1, 0, 0, 8, 0 }, { 9, 0, 0, 8, 6, 3, 0, 0, 5 }, { 0, 5, 0, 0, 9, 0, 6, 0, 0 }, { 1, 3, 0, 0, 0, 0, 2, 5, 0 }, { 0, 0, 0, 0, 0, 0, 0, 7, 4 }, { 0, 0, 5, 2, 0, 6, 3, 0, 0 } }; if (solveSuduko(grid, 0, 0)) print(grid); else cout << "no solution exists " << endl; return 0; } 2. Rata en el laberinto Analicemos Rata en el laberinto como otro problema de ejemplo que se puede resolver usando Backtracking. Un laberinto se da como una matriz binaria de bloques N * N donde el bloque de origen es el bloque superior izquierdo, es decir, el laberinto [0] [0] y el bloque de destino es el bloque inferior derecho, es decir, laberinto [N-1] [N-1] . Una rata parte de la fuente y tiene que llegar al destino. La rata solo puede moverse en dos direcciones: hacia adelante y hacia abajo. En la matriz de laberinto, 0 significa que el bloque es un callejón sin salida y 1 significa que el bloque se puede utilizar en la ruta desde el origen hasta el destino. Tenga en cuenta que esta es una versión simple del típico problema de Maze. Por ejemplo, una versión más compleja puede ser que la rata se pueda mover en 4 direcciones y una versión más compleja puede ser con un número limitado de movimientos. A continuación se muestra un ejemplo de laberinto. Los bloques grises son callejones sin salida (valor = 0).
  • 10. A continuación se muestra una representación matricial binaria del laberinto anterior. {1, 0, 0, 0} {1, 1, 0, 1} {0, 1, 0, 0} {1, 1, 1, 1} /* C++ program to solve Rat in a Maze problem using backtracking */ #include <stdio.h> // Maze size #define N 4 bool solveMazeUtil( int maze[N][N], int x, int y, int sol[N][N]); /* A utility function to print solution matrix sol[N][N] */ void printSolution(int sol[N][N]) { for (int i = 0; i < N; i++) { for (int j = 0; j < N; j++) printf(" %d ", sol[i][j]); printf("n"); } }
  • 11. /* A utility function to check if x, y is valid index for N*N maze */ bool isSafe(int maze[N][N], int x, int y) { // if (x, y outside maze) return false if ( x >= 0 && x < N && y >= 0 && y < N && maze[x][y] == 1) return true; return false; } /* This function solves the Maze problem using Backtracking. It mainly uses solveMazeUtil() to solve the problem. It returns false if no path is possible, otherwise return true and prints the path in the form of 1s. Please note that there may be more than one solutions, this function prints one of the feasible solutions.*/ bool solveMaze(int maze[N][N]) { int sol[N][N] = { { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, { 0, 0, 0, 0 } }; if (solveMazeUtil( maze, 0, 0, sol) == false) { printf("Solution doesn't exist"); return false; } printSolution(sol); return true; } /* A recursive utility function to solve Maze problem */ bool solveMazeUtil( int maze[N][N], int x, int y, int sol[N][N]) {
  • 12. // if (x, y is goal) return true if ( x == N - 1 && y == N - 1 && maze[x][y] == 1) { sol[x][y] = 1; return true; } // Check if maze[x][y] is valid if (isSafe(maze, x, y) == true) { // Check if the current block is already part of solution path. if (sol[x][y] == 1) return false; // mark x, y as part of solution path sol[x][y] = 1; /* Move forward in x direction */ if (solveMazeUtil( maze, x + 1, y, sol) == true) return true; /* If moving in x direction doesn't give solution then Move down in y direction */ if (solveMazeUtil( maze, x, y + 1, sol) == true) return true; /* If moving in y direction doesn't give solution then Move back in x direction */ if (solveMazeUtil( maze, x - 1, y, sol) == true) return true; /* If moving backwards in x direction doesn't give solution then Move upwards in y direction */ if (solveMazeUtil( maze, x, y - 1, sol) == true) return true;
  • 13. /* If none of the above movements work then BACKTRACK: unmark x, y as part of solution path */ sol[x][y] = 0; return false; } return false; } // driver program to test above function int main() { int maze[N][N] = { { 1, 0, 0, 0 }, { 1, 1, 0, 1 }, { 0, 1, 0, 0 }, { 1, 1, 1, 1 } }; solveMaze(maze); return 0; } 3. El problema del tour del caballo Planteamiento del problema: Dado un tablero N * N con el Caballo colocado en el primer bloque de un tablero vacío. Moverse de acuerdo con las reglas del caballo de ajedrez debe visitar cada casilla exactamente una vez. Imprime el orden de cada celda en la que se visitan. Ejemplo: Aporte: N = 8 Producción: 0 59 38 33 30 17 8 63 37 34 31 60 9 62 29 16 58 1 36 39 32 27 18 7 35 48 41 26 61 10 15 28 42 57 2 49 40 23 6 19 47 50 45 54 25 20 11 14 56 43 52 3 22 13 24 5 51 46 55 44 53 4 21 12 El camino que siguió el caballero para cubrir todas las celdas. A continuación se muestra un tablero de ajedrez con 8 x 8 celdas. Los números en las celdas indican el número de movimiento del Caballero.
  • 14. // C++ program for Knight Tour problem #include <bits/stdc++.h> using namespace std; #define N 8 int solveKTUtil(int x, int y, int movei, int sol[N][N], int xMove[], int yMove[]); /* A utility function to check if i,j are valid indexes for N*N chessboard */ int isSafe(int x, int y, int sol[N][N]) { return (x >= 0 && x < N && y >= 0 && y < N && sol[x][y] == -1); } /* A utility function to print solution matrix sol[N][N] */ void printSolution(int sol[N][N]) { for (int x = 0; x < N; x++) { for (int y = 0; y < N; y++) cout << " " << setw(2) << sol[x][y] << " "; cout << endl; } } /* This function solves the Knight Tour problem using Backtracking. This function mainly uses solveKTUtil() to solve the problem. It returns false if no complete tour is possible, otherwise return true and prints the tour. Please note that there may be more than one solutions,
  • 15. this function prints one of the feasible solutions. */ int solveKT() { int sol[N][N]; /* Initialization of solution matrix */ for (int x = 0; x < N; x++) for (int y = 0; y < N; y++) sol[x][y] = -1; /* xMove[] and yMove[] define next move of Knight. xMove[] is for next value of x coordinate yMove[] is for next value of y coordinate */ int xMove[8] = { 2, 1, -1, -2, -2, -1, 1, 2 }; int yMove[8] = { 1, 2, 2, 1, -1, -2, -2, -1 }; // Since the Knight is initially at the first block sol[0][0] = 0; /* Start from 0,0 and explore all tours using solveKTUtil() */ if (solveKTUtil(0, 0, 1, sol, xMove, yMove) == 0) { cout << "Solution does not exist"; return 0; } else printSolution(sol); return 1; } /* A recursive utility function to solve Knight Tour problem */ int solveKTUtil(int x, int y, int movei, int sol[N][N], int xMove[N], int yMove[N]) { int k, next_x, next_y; if (movei == N * N) return 1; /* Try all next moves from the current coordinate x, y */ for (k = 0; k < 8; k++) { next_x = x + xMove[k]; next_y = y + yMove[k]; if (isSafe(next_x, next_y, sol)) { sol[next_x][next_y] = movei;
  • 16. if (solveKTUtil(next_x, next_y, movei + 1, sol, xMove, yMove) == 1) return 1; else // backtracking sol[next_x][next_y] = -1; } } return 0; } // Driver Code int main() { // Function Call solveKT(); return 0; } Vuelta atrás o Backtracking para la enumeración El problema de la enumeración consiste en encontrar todas las soluciones del problema, es por ello que tendremos que recorrer el árbol de estados al completo. Algoritmo de Backtracking para la enumeración: proc Bactracking Enum(↕X[1 . . . i ]: TSolución, ↑num: N) variables L: ListaComponentes inicio si EsSolución (X) entonces num num+1 EscribeSolución (X) en otro caso L Candidatos (X) mientras ¬Vacía (L) hacer X[i + 1] Cabeza (L); L Resto (L) BacktrackingEnum (X, num) finmientras finsi fin
  • 17. Aplicaciones Vuelta atrás se usa en la implementación de los lenguajes de programación tales como Lenguaje de programación Planner y Prolog. Además, se usa en los análisis sintácticos de los compiladores. Su uso en inteligencia artificial ha sido muy importante, dando lugar a nuevos tipos de búsquedas como el A estrella. Branch & Bound (Ramificación y poda). Este método busca una solución como en el método de backtracking, pero cada solución tiene asociado un costo y la solución que se busca es una de mínimo costo llamada óptima. Además de ramificar una solución padre (branch) en hijos se trata de eliminar de consideración aquellos hijos cuyos descendientes tienen un costo que supera al óptimo buscado acotando el costo de los descendientes del hijo (bound). La forma de acotar es un arte que depende de cada problema. La acotación reduce el tiempo de búsqueda de la solución óptima al "podar" (pruning) los subarboles de descendientes costosos.