From 42d91390aa5ab07872dad13b560791e1b97256ab Mon Sep 17 00:00:00 2001 From: jpconsuegra Date: Sun, 31 Mar 2024 14:15:11 -0400 Subject: [PATCH] Add Lecture 10 (+Code) --- .../2024/10-combinatorial/code/Program.cs | 131 ++++++++++++++++ .../2024/10-combinatorial/code/src.csproj | 10 ++ .../2024/10-combinatorial/lecture-10.md | 146 ++++++++++++++++++ 3 files changed, 287 insertions(+) create mode 100644 conferences/2024/10-combinatorial/code/Program.cs create mode 100644 conferences/2024/10-combinatorial/code/src.csproj create mode 100644 conferences/2024/10-combinatorial/lecture-10.md diff --git a/conferences/2024/10-combinatorial/code/Program.cs b/conferences/2024/10-combinatorial/code/Program.cs new file mode 100644 index 0000000..25aff86 --- /dev/null +++ b/conferences/2024/10-combinatorial/code/Program.cs @@ -0,0 +1,131 @@ +namespace MatCom.Programming +{ + + class Combinatorial + { + public static void VariationsWithoutRepetitionsA(int[] items, int k, int pos = 0) + { + if (pos == k) + { + // item from 0 to k is a variation!!! + Console.WriteLine(string.Join(", ", items.Take(k))); + return; + } + + for (int i = pos; i < items.Length; i++) + { + int temp = items[pos]; + items[pos] = items[i]; + items[i] = temp; + + VariationsWithoutRepetitionsA(items, k, pos + 1); + + temp = items[pos]; + items[pos] = items[i]; + items[i] = temp; + } + } + + public static void VariationsWithoutRepetitionsB(int[] items, int k) + { + int[] variation = new int[k]; + bool[] taken = new bool[items.Length]; + InternalVariationsWithoutRepetitionsB(items, k, variation, 0, taken); + } + + static void InternalVariationsWithoutRepetitionsB(int[] items, int k, int[] variation, int count, bool[] taken) + { + if (count == k) + { + // variation is ready!!! + Console.WriteLine(string.Join(", ", variation.Take(count))); + return; + } + + for (int i = 0; i < items.Length; i++) + { + if (!taken[i]) + { + taken[i] = true; + variation[count] = items[i]; + InternalVariationsWithoutRepetitionsB(items, k, variation, count + 1, taken); + taken[i] = false; + } + } + } + + public static void Permutations(int[] items) + { + VariationsWithoutRepetitionsA(items, items.Length); + } + } + class TravelingSalesman + { + public static int Solve(int[,] distances) + { + int nCities = distances.GetLength(0); + int[] cities = Enumerable.Range(0, nCities).ToArray(); + int[] variation = new int[nCities]; + bool[] taken = new bool[nCities]; + return ModifiedVariationsWithoutRepetitionsB(cities, nCities, variation, 0, taken, distances, int.MaxValue); + } + + static int ModifiedVariationsWithoutRepetitionsB( + int[] items, + int k, + int[] variation, + int count, + bool[] taken, + int[,] distances, + int min + ) + { + if (count == k) + return Math.Min(min, EvaluateVariation(variation, distances)); + + for (int i = 0; i < items.Length; i++) + { + if (!taken[i]) + { + taken[i] = true; + variation[count] = items[i]; + min = ModifiedVariationsWithoutRepetitionsB(items, k, variation, count + 1, taken, distances, min); + taken[i] = false; + } + } + return min; + } + + static int EvaluateVariation(int[] variation, int[,] distances) + { + int result = 0; + for (int i = 0; i < variation.Length - 1; i++) + result += distances[variation[i], variation[i + 1]]; + return result; + } + } + + class Program + { + static void Main(string[] args) + { + int[] items = Enumerable.Range(0, 5).ToArray(); + Combinatorial.VariationsWithoutRepetitionsA(items, 2); + Console.WriteLine("--------------"); + Combinatorial.VariationsWithoutRepetitionsB(items, 2); + Console.WriteLine("--------------"); + Combinatorial.Permutations(items); + Console.WriteLine("--------------"); + + int[,] distances = new int[,] { + { 0, 60, 30, 40, 35 }, + { 60, 0, 15, 55, 30 }, + { 30, 15, 0, 75, 45 }, + { 40, 55, 75, 0, 85 }, + { 35, 30, 45, 85, 0 } + }; + int best = TravelingSalesman.Solve(distances); + Console.WriteLine(best); + } + } +} diff --git a/conferences/2024/10-combinatorial/code/src.csproj b/conferences/2024/10-combinatorial/code/src.csproj new file mode 100644 index 0000000..f02677b --- /dev/null +++ b/conferences/2024/10-combinatorial/code/src.csproj @@ -0,0 +1,10 @@ + + + + Exe + net7.0 + enable + enable + + + diff --git a/conferences/2024/10-combinatorial/lecture-10.md b/conferences/2024/10-combinatorial/lecture-10.md new file mode 100644 index 0000000..39e6f97 --- /dev/null +++ b/conferences/2024/10-combinatorial/lecture-10.md @@ -0,0 +1,146 @@ +# Recursividad (Combinatoria) + +La combinatoria, en programación, describe el proceso de resolver problemas a partir de explorar todas las posibles soluciones candidatas y seleccionar la óptima. + +A diferencia del caso más general de _backtrack_, el espacio de soluciones sigue una estructura combinatoria, esto es, son permutaciones, variaciones, combinaciones o subconjuntos de una colección de elementos. Estos principios se aplican mediante algoritmos recursivos que generan todas las posibles combinaciones del conjunto de elementos y de ahí el nombre. + +## Variantes generales de combinatoria + +- Permutaciones + - $n!$ +- Variaciones sin repeticiones + - $n! / (n-m)!$ +- Variaciones con repeticiones + - $n^m$ +- Combinaciones + - $n / (m! * (n-m)! )$ +- Subconjuntos + - $2^n$ + +## Ejemplos de Código + +### Variaciones sin repeticiones + +```csharp +void VariationsWithoutRepetitionsB(int[] items, int k) +{ + int[] variation = new int[k]; + bool[] taken = new bool[items.Length]; + InternalVariationsWithoutRepetitionsB(items, k, variation, 0, taken); +} + +void InternalVariationsWithoutRepetitionsB(int[] items, int k, int[] variation, int count, bool[] taken) +{ + if (count == k) + { + // variation is ready!!! + Console.WriteLine(string.Join(", ", variation.Take(count))); + return; + } + + for (int i = 0; i < items.Length; i++) + { + if (!taken[i]) + { + taken[i] = true; + variation[count] = items[i]; + InternalVariationsWithoutRepetitionsB(items, k, variation, count + 1, taken); + taken[i] = false; + } + } +} +``` + +### Permutaciones + +```csharp +void EasyPermutations(int[] items) +{ + VariationsWithoutRepetitionsA(items, items.Length); +} +``` + +```csharp +void InPlacePermutations(int[] items, int pos = 0) +{ + if(pos == items.Length) { + // item is a permutation!!! + Console.WriteLine(string.Join(", ", items)); + return; + } + + for(int i = pos; i < items.Length; i++) { + int temp = items[pos]; + items[pos] = items[i]; + items[i] = temp; + + Permutations(items, pos + 1); + + temp = items[pos]; + items[pos] = items[i]; + items[i] = temp; + } +} +``` + +### Problema del Viajante (Traveling Salesman Problem) +El problema del viajante es un problema clásico de optimización que busca encontrar la ruta más corta que visite todas las ciudades exactamente una vez. A continuación, se presenta un ejemplo básico de cómo resolver este problema utilizando fuerza bruta y recursividad. El objetivo es ilustrar cómo se puede modificar la plantilla base de combinatoria para ajustarla al problema. + +> **OJO:** En la práctica no se suele hacer la traducción directa pues estos algoritmos son muy ineficientes, así que se vuelve necesario computar las soluciones incrementalmente para poder hacer poda. +> +>> Con $5$ ciudades tenemos que explorar $120$ posibilidades. +>> +>> Pero con tan solo $20$ ciudades ya tenemos que explorar $2\,432902\,008176\,640000$ posibilidades! + +```csharp +class TravelingSalesman +{ + public static int Solve(int[,] distances) + { + int nCities = distances.GetLength(0); + int[] cities = Enumerable.Range(0, nCities).ToArray(); + int[] variation = new int[nCities]; + bool[] taken = new bool[nCities]; + return ModifiedVariationsWithoutRepetitionsB(cities, nCities, variation, 0, taken, distances, int.MaxValue); + } + + static int ModifiedVariationsWithoutRepetitionsB( + int[] items, + int k, + int[] variation, + int count, + bool[] taken, + int[,] distances, + int min + ) + { + if (count == k) + return Math.Min(min, EvaluateVariation(variation, distances)); + + for (int i = 0; i < items.Length; i++) + { + if (!taken[i]) + { + taken[i] = true; + variation[count] = items[i]; + min = ModifiedVariationsWithoutRepetitionsB(items, k, variation, count + 1, taken, distances, min); + taken[i] = false; + } + } + return min; + } + + static int EvaluateVariation(int[] variation, int[,] distances) + { + int result = 0; + for (int i = 0; i < variation.Length - 1; i++) + result += distances[variation[i], variation[i + 1]]; + return result; + } +} +``` + +## Consideraciones Generales +- **Eficiencia**: La combinatoria puede generar un gran número de combinaciones o permutaciones, por lo que es importante considerar la eficiencia de los algoritmos, especialmente en problemas con conjuntos de datos grandes. +- **Optimización**: Algunos problemas combinatorios pueden ser resueltos con algoritmos más eficientes, como el uso de programación dinámica en lugar de fuerza bruta. +- **Validación de Datos**: Es fundamental validar los datos de entrada para evitar problemas como desbordamientos de memoria o índices fuera de rango. \ No newline at end of file