WhatsApp
Ir al contenido

  

Algoritmo del Problema de la Mochila 0-1: Conceptos, Implementación en Python y Mejores Prácticas

Guía completa sobre el problema de la mochila 0-1, su formulación matemática, solución con programación dinámica en Python y técnicas avanzadas de optimización y depuración.

Problema de la Mochila 0-1 (0‑1 Knapsack)

1. ¿Qué es la mochila 0-1?

El problema de la mochila 0-1 es un clásico de la optimización combinatoria. Dado un conjunto de n ítems, cada uno con un peso w_i y un valor v_i, y una capacidad máxima W de la mochila, debemos seleccionar un subconjunto de ítems de forma que:

  • La suma de los pesos no supere W.
  • El valor total sea máximo.

La restricción "0‑1" indica que cada ítem puede estar una sola vez (0 = no tomado, 1 = tomado). No se permiten fracciones.

2. Formulación matemática

max  Σ v_i·x_i
s.t. Σ w_i·x_i ≤ W
     x_i ∈ {0,1}   ∀ i = 1…n

Donde x_i es la variable de decisión que indica si el ítem i se incluye.

3. Solución con Programación Dinámica (DP)

La DP construye una tabla dp[i][c] que representa el valor máximo alcanzable usando los primeros i ítems con capacidad c. La recurrencia es:

dp[i][c] = dp[i-1][c]                              // no tomar el ítem i
if w_i ≤ c:
    dp[i][c] = max(dp[i][c], dp[i-1][c-w_i] + v_i)   // tomar el ítem i

La complejidad temporal es O(n·W) y la espacial O(n·W). Con una optimización de espacio podemos reducirla a O(W) usando una sola fila y recorriendo la capacidad en sentido descendente.

4. Implementación en Python (versión clásica)

def knapsack_01(weights, values, capacity):
    n = len(weights)
    # tabla DP de (n+1) x (capacity+1)
    dp = [[0] * (capacity + 1) for _ in range(n + 1)]

    for i in range(1, n + 1):
        w = weights[i-1]
        v = values[i-1]
        for c in range(capacity + 1):
            dp[i][c] = dp[i-1][c]                 # no tomar
            if w <= c:
                dp[i][c] = max(dp[i][c], dp[i-1][c-w] + v)  # tomar
    return dp[n][capacity]

# Ejemplo de uso
weights = [2, 3, 4, 5]
values  = [3, 4, 5, 6]
capacity = 5
print(knapsack_01(weights, values, capacity))  # → 7 (ítems 1 y 2)

Salida esperada: 7 (seleccionamos los ítems con peso 2 y 3).

5. Optimización de espacio (O(W) memoria)

def knapsack_01_optimized(weights, values, capacity):
    n = len(weights)
    dp = [0] * (capacity + 1)
    for i in range(n):
        w, v = weights[i], values[i]
        # recorrer de mayor a menor para evitar reutilizar el ítem en la misma iteración
        for c in range(capacity, w-1, -1):
            dp[c] = max(dp[c], dp[c-w] + v)
    return dp[capacity]

print(knapsack_01_optimized(weights, values, capacity))  # → 7

Esta versión es ideal cuando W es grande y la tabla completa no cabe en memoria.

6. Recuperar los ítems seleccionados

def knapsack_items(weights, values, capacity):
    n = len(weights)
    dp = [[0] * (capacity + 1) for _ in range(n + 1)]
    for i in range(1, n + 1):
        w, v = weights[i-1], values[i-1]
        for c in range(capacity + 1):
            dp[i][c] = dp[i-1][c]
            if w <= c:
                dp[i][c] = max(dp[i][c], dp[i-1][c-w] + v)
    # backtrack para obtener los ítems
    res, c = [], capacity
    for i in range(n, 0, -1):
        if dp[i][c] != dp[i-1][c]:
            res.append(i-1)          # índice del ítem
            c -= weights[i-1]
    res.reverse()
    return dp[n][capacity], res

valor, items = knapsack_items(weights, values, capacity)
print('Valor máximo:', valor)
print('Ítems elegidos:', items)   # → [0, 1]

7. Comparativa con otras variantes

0‑1 Knapsack vs. Fraccionario
  • Tipo de decisión: Binaria vs. continua.
  • Complejidad: O(n·W) (NP‑completo) vs. O(n log n) (algoritmo voraz).
  • Aplicaciones: Selección de proyectos, carga de contenedores, empaquetado de recursos.
DP 0‑1 vs. Branch & Bound
  • Enfoque: Tabla completa vs. árbol de decisiones con poda.
  • Ventaja: DP garantiza solución óptima en tiempo predecible para W pequeño.
  • Ventaja de B&B: Escala mejor cuando W es enorme y los pesos son grandes.

8. Buenas prácticas y trucos de rendimiento

  • Ordenar por relación valor/peso: Útil antes de aplicar heurísticas o B&B.
  • Uso de numpy para la tabla: Operaciones vectorizadas pueden reducir el factor constante.
  • Memoria compartida en procesos paralelos: Cuando se evalúan múltiples instancias (ej. diferentes capacidades) se puede reutilizar la tabla parcial.
  • Limitar la capacidad: Redondear pesos a la unidad más grande que mantenga la precisión requerida reduce W y acelera DP.
import numpy as np

def knapsack_numpy(weights, values, capacity):
    dp = np.zeros(capacity + 1, dtype=int)
    for w, v in zip(weights, values):
        dp[w:] = np.maximum(dp[w:], dp[:-w] + v)
    return dp[capacity]

9. Depuración y troubleshooting comunes

ProblemaPosible causaSolución
Resultado negativoPesos o valores negativos en los datosValidar la entrada; filtrar o convertir a valores absolutos
Memoria agotadaCapacidad W demasiado grandeAplicar reducción de escala o usar algoritmo Branch & Bound
Ítems no coinciden con el valor óptimoBacktrack incorrecto (índice off‑by‑one)Revisar la lógica de recuperación, usar dp[i][c] != dp[i-1][c]

10. Extensiones y casos de uso reales

El algoritmo 0‑1 Knapsack aparece en:

  • Planificación de recursos en la nube: Asignar VMs a servidores físicos respetando límites de CPU y RAM.
  • Optimización de contenedores Docker/Podman: Seleccionar imágenes que maximizan funcionalidades dentro de un límite de tamaño de capa.
  • Selección de características en Machine Learning: Elegir subconjunto de variables que maximiza precisión bajo una restricción de coste computacional.

© 2025 BlogTech AI – Todos los derechos reservados.

 

Algoritmo del Problema de la Mochila 0-1: Conceptos, Implementación en Python y Mejores Prácticas
ASIMOV Ingeniería S. de R.L. de C.V., Emiliano Nava 15 de noviembre de 2025
Compartir
Iniciar sesión dejar un comentario

  
Algoritmo de la Subseccuencia Creciente Más Larga (LIS) en Python: Guía Completa y Ejemplos Prácticos
Aprende a resolver el problema de la Subseccuencia Creciente Más Larga (LIS) con Python. Explicación teórica, implementaciones O(N²) y O(N log N), casos de uso, comparativas y buenas prácticas.