Skip to content

Commit

Permalink
Add Lecture 11 (+Code)
Browse files Browse the repository at this point in the history
  • Loading branch information
jpconsuegra committed Apr 7, 2024
1 parent 8ebc7ae commit 955cae5
Show file tree
Hide file tree
Showing 3 changed files with 366 additions and 0 deletions.
166 changes: 166 additions & 0 deletions conferences/2024/11-dinamic-programming/code/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
using System.Diagnostics;

namespace MatCom.Programming
{
class Fibonacci
{
public static int Solve(int n)
{
if (n <= 1)
return 1;

int[] fib = new int[n + 1];
fib[0] = 1;
fib[1] = 1;

for (int i = 2; i <= n; i++)
{
fib[i] = fib[i - 1] + fib[i - 2];
}

return fib[n];
}
}
class ProblemaDeLaMochila
{
public static int Combinatoria(int[] ganancia, int[] peso, int capacidad, int n)
{
return n == 0 ?
0 :
peso[n - 1] > capacidad ?
Combinatoria(ganancia, peso, capacidad, n - 1) :
Math.Max(
Combinatoria(ganancia, peso, capacidad, n - 1),
Combinatoria(ganancia, peso, capacidad - peso[n - 1], n - 1) + ganancia[n - 1]
);
}

public static int DPMatriz(int[] ganancia, int[] peso, int capacidad, int n)
{
int[,] m = new int[n + 1, capacidad + 1];

for (int c = 0; c <= capacidad; c++)
m[0, c] = 0;

for (int i = 1; i <= n; i++)
for (int c = 0; c <= capacidad; c++)
{
m[i, c] = c < peso[i - 1] ?
m[i - 1, c] :
Math.Max(
m[i - 1, c],
m[i - 1, c - peso[i - 1]] + ganancia[i - 1]

);
}

return m[n, capacidad];
}

public static int DP2Filas(int[] ganancia, int[] peso, int capacidad, int n)
{
int[] completado = new int[capacidad + 1];
int[] actual = new int[capacidad + 1];
int[] aux;

for (int c = 0; c <= capacidad; c++)
completado[c] = 0;

for (int i = 1; i <= n; i++)
{
for (int c = capacidad; c >= 0; c--)
{
if (c < peso[i - 1])
actual[c] = completado[c];

else
actual[c] = Math.Max(
completado[c],
completado[c - peso[i - 1]] + ganancia[i - 1]
);
}
aux = completado;
completado = actual;
actual = aux;
}

return completado[capacidad];
}

public static int DP1Fila(int[] ganancia, int[] peso, int capacidad, int n)
{
int[] best = new int[capacidad + 1];

for (int c = 0; c <= capacidad; c++)
best[c] = 0;

for (int i = 1; i <= n; i++)
for (int c = capacidad; c >= 0; c--)
{
if (c < peso[i - 1])
continue;

best[c] = Math.Max(
best[c],
best[c - peso[i - 1]] + ganancia[i - 1]
);
}
return best[capacidad];
}
}

class Program
{
static void Evaluate(int[] ganancia, int[] peso, int capacidad, int n)
{
Stopwatch stopwatch = new Stopwatch();
int best;

Console.WriteLine("-----------------------------");

stopwatch.Start();
best = ProblemaDeLaMochila.Combinatoria(ganancia, peso, capacidad, n);
stopwatch.Stop();
Console.WriteLine($"Gain: {best}");
Console.WriteLine($"Execution time: {stopwatch.Elapsed.TotalSeconds} seconds");

stopwatch.Restart();
best = ProblemaDeLaMochila.DPMatriz(ganancia, peso, capacidad, n);
stopwatch.Stop();
Console.WriteLine($"Gain: {best}");
Console.WriteLine($"Execution time: {stopwatch.Elapsed.TotalSeconds} seconds");

stopwatch.Restart();
best = ProblemaDeLaMochila.DP2Filas(ganancia, peso, capacidad, n);
stopwatch.Stop();
Console.WriteLine($"Gain: {best}");
Console.WriteLine($"Execution time: {stopwatch.Elapsed.TotalSeconds} seconds");

stopwatch.Restart();
best = ProblemaDeLaMochila.DP1Fila(ganancia, peso, capacidad, n);
stopwatch.Stop();
Console.WriteLine($"Gain: {best}");
Console.WriteLine($"Execution time: {stopwatch.Elapsed.TotalSeconds} seconds");

Console.WriteLine("-----------------------------");
}
static void Main(string[] args)
{
Console.WriteLine(Fibonacci.Solve(5));

Evaluate(
ganancia: new int[] { 92, 57, 49, 68, 60, 43, 67, 84, 87, 72, },
peso: new int[] { 23, 31, 29, 44, 53, 38, 63, 85, 89, 82 },
capacidad: 165,
n: 10
);

Evaluate(
ganancia: new int[] { 15, 25, 35, 45, 55, 65, 75, 85, 95, 105, 115, 125, 135, 145, 155, 165, 175, 185, 195, 205, 215, 225, 235, 245, 255, 265, 275, 285, 295, 305 } ,
peso: new int [] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30 },
capacidad: 100,
n: 30
);
}
}
}
10 changes: 10 additions & 0 deletions conferences/2024/11-dinamic-programming/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>
190 changes: 190 additions & 0 deletions conferences/2024/11-dinamic-programming/lecture-11.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
# Programación Dinámica

La programación dinámica es una técnica de optimización utilizada en informática para resolver problemas que pueden ser divididos en subproblemas superpuestos y repetitivos. Su enfoque consiste en resolver cada subproblema solo una vez y luego almacenar su solución, evitando así recalcularla cada vez que se encuentre nuevamente.

### Estructura General

La estructura general de un algoritmo de programación dinámica implica los siguientes pasos:

1. **Definición del problema**: Identificar el problema y sus subproblemas.
2. **Formulación de la solución recursiva**: Plantear una solución recursiva para el problema que involucre soluciones a sus subproblemas.
3. **Memorización o tabla de memorización**: Almacenar las soluciones de los subproblemas en una tabla para evitar recálculos.
4. **Implementación de la solución**: Utilizar la tabla de memorización para resolver el problema de manera eficiente.

### Ejemplos de Código

#### Fibonacci

A continuación, se presenta un ejemplo simple de cómo se puede aplicar programación dinámica para resolver el problema de calcular el n-ésimo término de la secuencia de Fibonacci:

```csharp
public static int Fibonacci(int n)
{
if (n <= 1)
return 1;

int[] fib = new int[n + 1];
fib[0] = 1;
fib[1] = 1;

for (int i = 2; i <= n; i++)
{
fib[i] = fib[i - 1] + fib[i - 2];
}

return fib[n];
}
```

#### Problema de la Mochila

El problema de la mochila es un problema clásico de optimización en informática y combinatoria.
En su variante 0-1, se plantea de la siguiente manera:

> Supongamos que tienes una mochila con una capacidad máxima dada y una serie de objetos, cada uno con un peso y un valor asociado.
> El objetivo es determinar la combinación de objetos que puedes colocar en la mochila para maximizar el valor total,
> sin exceder la capacidad máxima de la mochila.
> Se pude asumir que no hay objetos con peso nulo.
A continuación, se presenta un ejemplo simple de cómo se puede aplicar cominatoria para resolver el problema.

```csharp
// ganancia maxima que se puede obtener de n objetos y una mochila con cierta capacidad sin repetir objetos
public static int Mochila(int[] ganancia, int[] peso, int capacidad, int n)
{
return n == 0 ? // Si no hay objetos entre los que elegir (si no pueden haber objetos de peso 0
// poner capacidad == 0 para podar)
0 : // Entonces la ganancia maxima es 0, sino
peso[n - 1] > capacidad ? // Si el peso del objeto n-esimo sobrepasa la capacidad de la mochila
// Entonces no se puede tomar, y por tanto es
// la ganacia maxima con los 1ros n - 1 objetos
Mochila(ganancia, peso, capacidad, n - 1) :
Math.Max( // Sino, es el maximo entre
Mochila(ganancia, peso, capacidad, n - 1), // no haberlo tomado y
Mochila(ganancia, peso, capacidad - peso[n - 1], n - 1) + ganancia[n - 1] // Tomarlo más maximizar
// una mochila con capacidad
// y objetos restantes
);
}
```

A continuación, se presenta un ejemplo simple de cómo se puede aplicar programación dinámica para resolver el problema.

```csharp
// ganancia maxima que se puede obtener de n objetos y una mochila con cierta capacidad sin repetir objetos
public static int MochilaDPMatriz(int[] ganancia, int[] peso, int capacidad, int n)
{
int[,] m = new int[n + 1, capacidad + 1]; // m[i,j] = ganancia maxima para una mochila
// con capacidad j pudiendo elegir entre i objetos
for (int c = 0; c <= capacidad; c++) // Si no hay objetos, no importa la capacidad de la mochila
m[0, c] = 0; // La ganancia maxima va ha ser cero
for (int i = 1; i <= n; i++) // Para el resto de las posibles cantidades de objetos
for (int c = 0; c <= capacidad; c++) // Y para todas las capacidades
{
m[i, c] = c < peso[i - 1] ? // Si el objeto i-esimo pesa más que la capacidad actual
m[i - 1, c] : // Entonces no se puede tomar, sino
Math.Max( // Es lo mejor entre
m[i - 1, c], // No haberlo tomado (se mantiene la misma capacidad)
m[i - 1, c - peso[i - 1]] + ganancia[i - 1] // Y haberlo tomado (la capacidad se reduce según
// su peso y ganancia aumenta según la del objeto)
);
}

return m[n, capacidad]; // Devolver la ganancia maxima para una mochila con
// la capacidad dada pudiendo elegir entre n objetos
}
```

A continuación, se presenta una optimización al aplicar programación dinámica para resolver el problema.
La memoria se reduce a dos filas.

```csharp
// ganancia maxima que se puede obtener de n objetos y una mochila con cierta capacidad sin repetir objetos
public static int MochilaDP2Filas(int[] ganancia, int[] peso, int capacidad, int n)
{
int[] completado = new int[capacidad + 1]; // Tras haber analizado i objetos completado[c] contiene
// la ganancia maxima que se puede obtener con i objetos
// y una mochila de capacidad c sin repetir objetos
int[] actual = new int[capacidad + 1]; // Tras haber analizado i objetos actual[c] contiene
// la ganancia maxima que se puede obtener con i - 1 objetos
// y una mochila de capacidad c sin repetir objetos
int[] aux; // Para no perder las referencias durante el swap y tener que reservar más memoria
for (int c = 0; c <= capacidad; c++) // Si no hay objetos, no importa la capacidad de la mochila
completado[c] = 0; // La ganancia maxima va ha ser cero
for (int i = 1; i <= n; i++) // Para el resto de las posibles cantidades de objetos
{
for (int c = capacidad; c >= 0; c--) // Y para todas las capacidades
{
if (c < peso[i - 1]) // Si el objeto i-esimo pesa más que la capacidad actual
actual[c] = completado[c]; // Entonces optimizar sin haberlo tomado
// (completado[c] contiene el de i - 1 objetos)
else
actual[c] = Math.Max( // Sino, es lo mejor entre
completado[c], // No haberlo tomado (completado[c] contiene el de i - 1 objetos)
completado[c - peso[i - 1]] + ganancia[i - 1] // Y haberlo tomado (la capacidad se reduce
// segun su peso y ganancia aumenta según la
// del objeto)
// (completado[c - peso[i - 1]] contiene el
// optimo de i - 1 objetos)
);
}
aux = completado; // Guardar puntero (referencia) al resultado de la iteración anterior
completado = actual; // Poner referencia del resultado de esta iteración en completado[]
actual = aux; // Recuperar referencia para reescribir (reutilizar) en la proxima iteracion
} // Cada iteración finalizada pone en completado la ganancia maxima hasta con i objetos para cada capacidad
return completado[capacidad]; // Devolver la ganancia maxima para una mochila con la capacidad dada
// pudiendo elegir entre n objetos
}
```

A continuación, se presenta una optimización al aplicar programación dinámica para resolver el problema.
La memoria se reduce a una fila.

```csharp
// ganancia maxima que se puede obtener de n objetos y una mochila con cierta capacidad sin repetir objetos
public static int MochilaDP1Fila(int[] ganancia, int[] peso, int capacidad, int n)
{
int[] best = new int[capacidad + 1]; // Tras haber analizado i objetos best[c] contiene
// la ganancia maxima que se puede obtener con i objetos y
// una mochila de capacidad c sin repetir objetos
for (int c = 0; c <= capacidad; c++) // Si no hay objetos, no importa la capacidad de la mochila
best[c] = 0; // La ganancia maxima va ha ser cero
for (int i = 1; i <= n; i++) // Para el resto de las posibles cantidades de objetos
for (int c = capacidad; c >= 0; c--) // Y para todas las capacidades
{
if (c < peso[i - 1]) // Si el objeto i-esimo pesa más que la capacidad actual
continue; // Entonces best[c] = best[c] (no tomarlo)
best[c] = Math.Max( // Sino, es lo mejor entre
best[c], // No haberlo tomado
// (best[c] contiene aun valor de la iteracion anterior)
best[c - peso[i - 1]] + ganancia[i - 1] // Y haberlo tomado (la capacidad se reduce
// segun su peso y ganancia aumenta según la
// del objeto)
// (best[c - peso[i - 1]] sigue conteniendo
// el optimo para i - 1 objetos porque
// c - peso[i - 1] <= c)
);
}
return best[capacidad]; // Devolver la ganancia maxima para una mochila con la capacidad dada
// pudiendo elegir entre n objetos
}
```

### Consejos

La programación dinámica es una herramienta poderosa para resolver problemas complejos de manera eficiente, pero su aplicación requiere comprensión y cuidado para evitar posibles trampas de rendimiento y memoria.

- **Comprender el problema**: Antes de aplicar programación dinámica, es esencial entender completamente el problema y sus subproblemas.
- **Empezar con casos simples**: Comenzar resolviendo casos simples del problema y luego generalizar a casos más complejos puede ayudar a entender mejor la estructura de la solución.
- **Optimización de memoria**: Si el espacio de memoria es una preocupación, considerar técnicas de optimización de memoria como utilizar matrices de menor tamaño o implementar una tabla de memorización de manera más eficiente.

0 comments on commit 955cae5

Please sign in to comment.