Skip to content

Commit

Permalink
Add Lecture 09 (+Code)
Browse files Browse the repository at this point in the history
  • Loading branch information
jpconsuegra committed Mar 24, 2024
1 parent 2dcfe5b commit ba8d894
Show file tree
Hide file tree
Showing 3 changed files with 246 additions and 0 deletions.
132 changes: 132 additions & 0 deletions conferences/2024/09-backtrack/code/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
namespace MatCom.Programming
{
class NQueensProblem
{
public static bool Solve(int nQueens)
{
return PlaceQueen(new int[nQueens], 0);
}

static bool PlaceQueen(int[] queenColumnPerRow, int row)
{
if (row == queenColumnPerRow.Length)
{
PrintSolution(queenColumnPerRow);
return true;
}
for (int col = 0; col < queenColumnPerRow.Length; col++)
{
if (IsSafe(queenColumnPerRow, row, col))
{
queenColumnPerRow[row] = col;
if (PlaceQueen(queenColumnPerRow, row + 1))
return true;
}
}
return false;
}

static bool IsSafe(int[] queenColumnPerRow, int row, int col)
{
for (int prevRow = 0; prevRow < row; prevRow++)
{
int prevCol = queenColumnPerRow[prevRow];
if (prevCol == col || Math.Abs(prevCol - col) == Math.Abs(prevRow - row))
return false;
}
return true;
}

static void PrintSolution(int[] queenColumnPerRow)
{
for (int i = 0; i < queenColumnPerRow.Length; i++)
{
Console.Write("|");
for (int j = 0; j < queenColumnPerRow.Length; j++)
{
if (queenColumnPerRow[i] == j)
Console.Write("♛ |");
else
Console.Write("  |");
}
Console.WriteLine();
}
Console.WriteLine();
}
}

class MazeSolver
{
public static bool Solve(bool[,] maze, int x, int y, out bool[,] solution)
{
solution = new bool[maze.GetLength(0), maze.GetLength(1)];
return SolveMaze(maze, x, y, solution);
}

static bool SolveMaze(bool[,] maze, int x, int y, bool[,] solution)
{
if (x < 0 || x >= maze.GetLength(0) || y < 0 || y >= maze.GetLength(1) || maze[x, y] || solution[x, y])
return false;

solution[x, y] = true;

if (x == maze.GetLength(0) - 1 && y == maze.GetLength(1) - 1)
return true;

if (
SolveMaze(maze, x + 1, y, solution) ||
SolveMaze(maze, x, y + 1, solution) ||
SolveMaze(maze, x - 1, y, solution) ||
SolveMaze(maze, x, y - 1, solution)
)
return true;

solution[x, y] = false;
return false;
}

public static void PrintSolution(bool[,] maze, bool[,] solution)
{
for (int i = 0; i < maze.GetLength(0); i++)
{
Console.Write("| ");
for (int j = 0; j < maze.GetLength(1); j++)
{
string symbol = maze[i, j] ? "⬛" : solution[i, j] ? "ꆜ" : "ㅤ";
Console.Write(symbol + " ");
}
Console.WriteLine("|");
}
}
}

class Program
{


static void Main(string[] args)
{
NQueensProblem.Solve(8);

bool[,] maze = new bool[,] {
{ false, true, false, false, false },
{ false, false, false, true, false },
{ true, true, true, false, false },
{ true, true, true, false, true },
{ false, false, false, false, false }
};
bool[,] solution;

if (MazeSolver.Solve(maze, 0, 0, out solution))
{
MazeSolver.PrintSolution(maze, solution);
}
else
{
Console.WriteLine("No solution exists.");
}
}


}
}
10 changes: 10 additions & 0 deletions conferences/2024/09-backtrack/code/src.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

</Project>
104 changes: 104 additions & 0 deletions conferences/2024/09-backtrack/lecture-09.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# Recursividad (Backtracking)

El backtracking es una técnica de búsqueda exhaustiva que se utiliza para encontrar todas las soluciones posibles para un problema, explorando de manera sistemática el espacio de soluciones. Se basa en la idea de construir soluciones de manera incremental y realizar retrocesos (backtracks) cuando se alcanzan caminos sin salida, lo que permite encontrar la solución óptima o todas las soluciones posibles.

A diferencia de los enfoques vistos en las clases anteriores, al utilizar backtrack cada llamado recursivo no necesariamente nos acerca más a la solución. O sea, existen llamados recursivos que no formarán parte de la solución final, cosa que no pasaba con los enfoques básicos o divide y vencerás.

## Estructura General

El algoritmo de backtracking sigue una estructura general recursiva:

1. **Definir la función de búsqueda**: Esta función recibe como parámetro el estado actual de la solución parcial y realiza una serie de pasos para construir la solución final.
2. **Evaluar condición de término**: Se verifica si la solución parcial es válida y si se ha alcanzado la solución final.
3. **Construir soluciones parciales**: Se generan todas las posibles extensiones de la solución parcial actual.
4. **Realizar retroceso (backtrack)**: Cuando no es posible extender más la solución parcial, se deshace la última elección y se retrocede a un estado anterior.
5. **Llamada recursiva**: Se llama a la función de búsqueda con la solución parcial actualizada.

## Ejemplos de Código

### Problema de las 8 reinas

```csharp
class NQueensProblem
{
public static bool Solve(int nQueens)
{
return PlaceQueen(new int[nQueens], 0);
}

static bool PlaceQueen(int[] queenColumnPerRow, int row)
{
if (row == queenColumnPerRow.Length)
{
return true;
}
for (int col = 0; col < queenColumnPerRow.Length; col++)
{
if (IsSafe(queenColumnPerRow, row, col))
{
queenColumnPerRow[row] = col;
if (PlaceQueen(queenColumnPerRow, row + 1))
return true;
}
}
return false;
}

static bool IsSafe(int[] queenColumnPerRow, int row, int col)
{
for (int prevRow = 0; prevRow < row; prevRow++)
{
int prevCol = queenColumnPerRow[prevRow];
if (prevCol == col || Math.Abs(prevCol - col) == Math.Abs(prevRow - row))
return false;
}
return true;
}
}
```

### Salida del laberinto

```csharp
class MazeSolver {

public static bool Solve(bool[,] maze, int x, int y, out bool[,] solution)
{
solution = new bool[maze.GetLength(0), maze.GetLength(1)];
return SolveMaze(maze, x, y, solution);
}

static bool SolveMaze(bool[,] maze, int x, int y, bool[,] solution)
{
if (x < 0 || x >= maze.GetLength(0) || y < 0 || y >= maze.GetLength(1) || maze[x, y] || solution[x, y])
return false;

solution[x, y] = true;

if (x == maze.GetLength(0) - 1 && y == maze.GetLength(1) - 1)
return true;

if (
SolveMaze(maze, x + 1, y, solution) ||
SolveMaze(maze, x, y + 1, solution) ||
SolveMaze(maze, x - 1, y, solution) ||
SolveMaze(maze, x, y - 1, solution)
)
return true;

solution[x, y] = false;
return false;
}
}
```

## Consideraciones Generales

- **Complejidad temporal**: El backtracking puede tener una complejidad temporal exponencial en el peor de los casos, por lo que es importante implementar podas y optimizaciones para mejorar el rendimiento.
- **Uso eficiente de la memoria**: Es esencial optimizar el uso de la memoria, especialmente en problemas con un gran espacio de soluciones, para evitar desbordamientos de pila o agotamiento de recursos.
- **Identificación de soluciones parciales válidas**: Definir correctamente las condiciones para determinar si una solución parcial es válida resulta crucial para el funcionamiento correcto del algoritmo de backtracking.

## Consejos

- **Implementar podas (pruning)**: Identificar y eliminar ramas del árbol de búsqueda que no conducen a una solución válida puede reducir significativamente el tiempo de ejecución.
- **Ordenar las opciones de búsqueda**: En algunos casos, ordenar las opciones de búsqueda puede mejorar el rendimiento del algoritmo de backtracking al reducir el número de ramas exploradas.

0 comments on commit ba8d894

Please sign in to comment.