Gestión de la Intratabilidad: Más Allá de NP-Completitud
Publicado el 1 de diciembre de 2025
Gestión de la Intratabilidad: Estrategias Post-NP-Completo
Una vez que probamos que un problema es NP-completo, el trabajo apenas comienza. Los problemas reales no desaparecen por ser difíciles. Este capítulo explora estrategias prácticas para obtener soluciones útiles.
1. Definición: El Desafío de la Intratabilidad
Problemas NP-Completos en la Práctica
Realidad: Demostrar que un problema es NP-completo NO es el final.
Situación:
- No existe algoritmo polinomial exacto (probablemente)
- Pero el problema real sigue existiendo
- Se necesita una solución práctica
Pregunta clave: ¿Qué hacemos cuando la solución óptima es inalcanzable?
Limitación de la Teoría
Lo que NP-Completitud Establece
La teoría de NP-Completitud establece la dificultad de obtener:
- La respuesta exacta
- En tiempo polinomial
- Para el peor caso
Pero NO dice nada sobre:
- Aproximaciones cercanas
- Casos promedio
- Instancias específicas
- Soluciones subóptimas aceptables
2. Propósito: Soluciones Prácticas y Útiles
Objetivo: Soluciones Computables
Objetivo: Encontrar métodos que ofrezcan soluciones útiles y computables en la práctica.
Requisitos:
- Tiempo de ejecución razonable
- Calidad de solución aceptable
- Idealmente, con garantías demostrables
- Aplicable a instancias reales
Tipos de Garantías
| Tipo de Solución | Garantía | Tiempo | Optimalidad |
|---|---|---|---|
| Exacta | Solución óptima | Exponencial | 100% |
| Aproximación | Factor constante del óptimo | Polinomial | ≥ X% |
| Heurística | Sin garantía formal | Rápido | Variable |
| Caso promedio | Buena en promedio | Variable | Depende |
3. Tres Estrategias Principales
Vías para Abordar Problemas Difíciles
1. Algoritmos Rápidos en Caso Promedio
- Backtracking con poda sustancial
- Funciona bien en instancias típicas
- Sin garantías de peor caso
2. Heurísticas
- Métodos rápidos sin garantías
- Simulated Annealing, algoritmos genéticos
- Solución “suficientemente buena” en práctica
3. Algoritmos de Aproximación
- Garantía demostrable de calidad
- Factor de aproximación acotado
- Balance entre velocidad y garantías
═══════════════════════════════════════════════════════════
ESTRATEGIAS PARA PROBLEMAS NP-COMPLETOS
═══════════════════════════════════════════════════════════
1. CASO PROMEDIO (Backtracking optimizado):
Complejidad: Exponencial teórica
Práctica: Rápido en instancias típicas
Garantía: Solución ÓPTIMA (si termina)
Problema: Peor caso sigue siendo exponencial
Ejemplo: Sudoku con poda inteligente
- Peor caso: horas
- Caso típico: < 1 segundo
2. HEURÍSTICAS (Sin garantías):
Complejidad: Polinomial o mejor
Práctica: Muy rápido
Garantía: NINGUNA formal
Ventaja: Funciona bien en práctica
Ejemplo: Hill Climbing para TSP
- Tiempo: O(n²)
- Calidad: 10-30% sobre óptimo típico
- Riesgo: Óptimos locales
3. APROXIMACIÓN (Con garantías):
Complejidad: Polinomial
Práctica: Eficiente
Garantía: Factor α del óptimo
Trade-off: Nunca óptima, pero cerca
Ejemplo: 2-aproximación para Vertex Cover
- Tiempo: O(|E|)
- Calidad: ≤ 2 × óptimo SIEMPRE
- Predecible: Peor caso acotado
4. Algoritmos de Aproximación
Aproximación con Garantías
Algoritmo de Aproximación: Método polinomial que garantiza solución dentro de un factor de aproximación del óptimo.
Factor de aproximación α:
- Para minimización: (solución_aprox / óptimo)
- Para maximización: (óptimo / solución_aprox)
Ventaja clave: Garantía válida para TODAS las instancias, no solo casos típicos.
Ejemplo Clásico: Vertex Cover
Vertex Cover (Cubierta de Vértices)
Problema: Encontrar el subconjunto más pequeño de vértices que toca cada arista.
NP-Completo: No hay algoritmo polinomial exacto (probablemente)
Solución: Algoritmo de 2-aproximación
def vertex_cover_approx(graph):
"""
Algoritmo de 2-aproximación para Vertex Cover
Garantía: |solución| ≤ 2 × |óptimo|
Tiempo: O(|E|) - lineal en aristas
Args:
graph: grafo con aristas
Returns:
cover: conjunto de vértices (cubierta)
"""
cover = set()
edges = set(graph.edges) # Copiar aristas
print("Algoritmo de 2-aproximación para Vertex Cover\n")
print("Paso a paso:\n")
step = 1
while edges:
# Seleccionar arista arbitraria
u, v = edges.pop()
print(f"Paso {step}: Seleccionar arista ({u}, {v})")
print(f" Agregar vértices {u} y {v} a la cubierta")
# Agregar ambos extremos a la cubierta
cover.add(u)
cover.add(v)
# Eliminar todas las aristas incidentes a u o v
edges_to_remove = {e for e in edges if u in e or v in e}
edges -= edges_to_remove
print(f" Eliminar {len(edges_to_remove)} aristas cubiertas")
print(f" Aristas restantes: {len(edges)}")
print()
step += 1
return cover
# Ejemplo de uso
print("="*60)
print("VERTEX COVER: ALGORITMO DE 2-APROXIMACIÓN")
print("="*60 + "\n")
class Graph:
def __init__(self):
self.vertices = set()
self.edges = []
def add_edge(self, u, v):
self.vertices.add(u)
self.vertices.add(v)
self.edges.append((u, v))
def __repr__(self):
return f"Vértices: {sorted(self.vertices)}\nAristas: {self.edges}"
# Crear grafo de ejemplo
G = Graph()
edges_input = [(1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (1, 6), (2, 4)]
for u, v in edges_input:
G.add_edge(u, v)
print("Grafo:")
print(G)
print()
print("Visualización:")
print("""
1 --- 2
| |\\
| | 3
| |/
6 --- 5 --- 4
""")
print("="*60)
# Ejecutar algoritmo
cover = vertex_cover_approx(G)
print("="*60)
print("RESULTADO:")
print("="*60)
print(f"Cubierta encontrada: {sorted(cover)}")
print(f"Tamaño: {len(cover)}")
# Verificar que es cubierta válida
print("\nVerificación:")
for u, v in edges_input:
if u in cover or v in cover:
print(f" Arista ({u},{v}): ✓ cubierta por {u if u in cover else v}")
else:
print(f" Arista ({u},{v}): ✗ NO cubierta")
print("\n" + "="*60)
print("ANÁLISIS DE GARANTÍA:")
print("="*60)
print("""
¿Por qué es 2-aproximación?
Observación clave: Cada arista seleccionada requiere AL MENOS
uno de sus extremos en el óptimo.
Si seleccionamos k aristas:
- Agregamos 2k vértices a nuestra solución
- El óptimo necesita al menos k vértices
- Ratio: 2k / k = 2
Por tanto: |solución| ≤ 2 × |óptimo|
En este ejemplo:
- Nuestra solución: 6 vértices
- Óptimo probable: 3-4 vértices
- Ratio: 6/3 = 2 o 6/4 = 1.5
- Dentro de la garantía ✓
""") Teorema: El algoritmo de aproximación para Vertex Cover es una 2-aproximación.
Demostración:
Sea:
- = cubierta producida por el algoritmo
- = cubierta óptima (mínima)
- = conjunto de aristas seleccionadas durante el algoritmo
Observación 1:
- Por cada arista en , agregamos exactamente 2 vértices
Observación 2: Las aristas en no comparten vértices
- Cuando seleccionamos , eliminamos todas las aristas incidentes a y
- Ninguna arista futura puede compartir extremos con
Observación 3:
- Cada arista en debe ser cubierta por
- Como las aristas en no comparten vértices
- Se necesita AL MENOS un vértice distinto por arista
- Por tanto:
Conclusión:
Por tanto, el algoritmo es una 2-aproximación. □
¿Qué garantiza un algoritmo de α-aproximación?
5. Heurísticas: Simulated Annealing
Simulated Annealing (Recocido Simulado)
Heurística de búsqueda inspirada en el proceso metalúrgico de recocido.
Idea central: Permitir movimientos “cuesta arriba” ocasionales para escapar de óptimos locales.
Analogía: Enfriar metal lentamente para alcanzar estado de mínima energía.
Conceptos Fundamentales
Componentes de Simulated Annealing
Requisitos:
- Representación concisa del estado (solución candidata)
- Función de costo para evaluar calidad
- Función de vecindad (transiciones posibles)
- Programa de enfriamiento (schedule)
Componentes Clave:
1. Temperatura (T):
- Controla probabilidad de aceptar empeoramientos
- Alta T → acepta casi todo (exploración)
- Baja T → solo mejoras (explotación)
2. Criterio de Aceptación:
- Mejora (Δcost < 0): SIEMPRE aceptar
- Empeoramiento (Δcost > 0): aceptar con probabilidad exponencial decreciente según temperatura
3. Programa de Enfriamiento:
- Temperatura inicial: (típicamente 1.0)
- Decremento: ()
- Criterio de parada: o iteraciones
Pseudocódigo:
estado = solución_inicial()
T = T_inicial
mientras T > T_min:
para i = 1 hasta iter_por_temp:
nuevo = vecino(estado)
Δ = costo(nuevo) - costo(estado)
si Δ < 0: # Mejora
estado = nuevo
sino: # Empeoramiento
si random() < e^(-Δ/T):
estado = nuevo # Aceptar ocasionalmente
T = α × T # Enfriar
retornar estado Implementación Completa para TSP
import random
import math
def tsp_cost(tour, distances):
"""Calcular costo total del tour"""
cost = 0
n = len(tour)
for i in range(n):
cost += distances[tour[i]][tour[(i + 1) % n]]
return cost
def two_opt_swap(tour, i, j):
"""Generar vecino intercambiando segmento [i:j]"""
new_tour = tour[:i] + tour[i:j+1][::-1] + tour[j+1:]
return new_tour
def simulated_annealing_tsp(distances, T_initial=1.0, T_min=0.001,
alpha=0.95, iter_per_temp=100):
"""
Simulated Annealing para TSP
Args:
distances: matriz de distancias
T_initial: temperatura inicial
T_min: temperatura mínima
alpha: factor de enfriamiento
iter_per_temp: iteraciones por temperatura
Returns:
mejor_tour, mejor_costo, historia
"""
n = len(distances)
# Solución inicial: orden aleatorio
current_tour = list(range(n))
random.shuffle(current_tour)
current_cost = tsp_cost(current_tour, distances)
# Mejor solución encontrada
best_tour = current_tour[:]
best_cost = current_cost
# Historia para visualización
history = [(0, current_cost, best_cost)]
T = T_initial
iteration = 0
print(f"Iniciando SA para TSP con {n} ciudades")
print(f"Costo inicial: {current_cost:.2f}\n")
while T > T_min:
improvements_at_T = 0
for _ in range(iter_per_temp):
iteration += 1
# Generar vecino (2-opt swap)
i = random.randint(0, n - 2)
j = random.randint(i + 1, n - 1)
new_tour = two_opt_swap(current_tour, i, j)
new_cost = tsp_cost(new_tour, distances)
delta = new_cost - current_cost
# Decidir aceptación
if delta < 0:
# Mejora: siempre aceptar
current_tour = new_tour
current_cost = new_cost
improvements_at_T += 1
# Actualizar mejor
if current_cost < best_cost:
best_tour = current_tour[:]
best_cost = current_cost
else:
# Empeoramiento: aceptar con probabilidad
acceptance_prob = math.exp(-delta / T)
if random.random() < acceptance_prob:
current_tour = new_tour
current_cost = new_cost
history.append((iteration, current_cost, best_cost))
# Reportar progreso
print(f"T={T:.4f} | Costo actual: {current_cost:.2f} | "
f"Mejor: {best_cost:.2f} | Mejoras: {improvements_at_T}")
# Enfriar
T *= alpha
print(f"\n{'='*60}")
print(f"Finalizado: Mejor costo = {best_cost:.2f}")
return best_tour, best_cost, history
# Ejemplo de uso
print("="*60)
print("SIMULATED ANNEALING PARA TSP")
print("="*60 + "\n")
# Crear instancia TSP pequeña
n = 8
distances = [[0] * n for _ in range(n)]
# Generar distancias aleatorias simétricas
random.seed(42)
for i in range(n):
for j in range(i + 1, n):
dist = random.randint(10, 100)
distances[i][j] = dist
distances[j][i] = dist
print(f"Instancia TSP con {n} ciudades")
print("\nMatriz de distancias:")
print(f"{'':>4}", end="")
for i in range(n):
print(f"{i:>4}", end="")
print()
for i in range(n):
print(f"{i:>3}", end=" ")
for j in range(n):
if i == j:
print(f"{'·':>4}", end="")
else:
print(f"{distances[i][j]:>4}", end="")
print()
print("\n" + "="*60)
print("EJECUTANDO SIMULATED ANNEALING:")
print("="*60 + "\n")
# Ejecutar SA
best_tour, best_cost, history = simulated_annealing_tsp(
distances,
T_initial=10.0,
T_min=0.01,
alpha=0.95,
iter_per_temp=50
)
print("\n" + "="*60)
print("RESULTADO FINAL:")
print("="*60)
print(f"Mejor tour: {best_tour}")
print(f"Costo: {best_cost:.2f}")
# Comparar con greedy
print("\n" + "="*60)
print("COMPARACIÓN CON GREEDY:")
print("="*60)
def greedy_tsp(distances):
"""TSP greedy: siempre ir a ciudad más cercana no visitada"""
n = len(distances)
tour = [0]
unvisited = set(range(1, n))
while unvisited:
current = tour[-1]
nearest = min(unvisited, key=lambda city: distances[current][city])
tour.append(nearest)
unvisited.remove(nearest)
return tour, tsp_cost(tour, distances)
greedy_tour, greedy_cost = greedy_tsp(distances)
print(f"Greedy cost: {greedy_cost:.2f}")
print(f"SA cost: {best_cost:.2f}")
print(f"Mejora: {((greedy_cost - best_cost) / greedy_cost * 100):.1f}%") Por Qué Funciona SA
Ventajas de Simulated Annealing
1. Escapa de Óptimos Locales
- Acepta empeoramientos ocasionales
- Alta temperatura: exploración amplia
- Baja temperatura: refinamiento local
2. Balance Exploración/Explotación
- Inicio: explorar espacio de búsqueda
- Final: converger a óptimo local
3. Fundamento Teórico
- Con enfriamiento infinitamente lento → óptimo global
- En práctica: soluciones muy buenas
4. Versátil
- Aplicable a cualquier problema con:
- Representación de estado
- Función de costo
- Función de vecindad
Comparación de Métodos Heurísticos para TSP:
MÉTODO 1: RANDOM SAMPLING (Muestreo Aleatorio)
Estrategia: Generar tours aleatorios, quedarse con el mejor
Pros: Simple, fácil de implementar
Contras: Muy ineficiente, resultados mediocres
Resultados típicos (50 ciudades):
- 100 muestras: 40-50% sobre óptimo
- 10,000 muestras: 25-35% sobre óptimo
- Nunca encuentra cerca del óptimo
MÉTODO 2: HILL CLIMBING (Subida de Colina)
Estrategia: Siempre aceptar mejoras, rechazar empeoramientos
Pros: Converge rápido, fácil de implementar
Contras: Se queda en primer óptimo local
Resultados típicos (50 ciudades):
- Mejora: 15-25% sobre óptimo
- Problema: Depende mucho del punto inicial
- Se estanca rápidamente
MÉTODO 3: SIMULATED ANNEALING
Estrategia: Aceptar empeoramientos con probabilidad decreciente
Pros: Escapa óptimos locales, robusto
Contras: Requiere ajuste de parámetros
Resultados típicos (50 ciudades):
- Mejora: 3-8% sobre óptimo
- Consistente en múltiples ejecuciones
- Balance exploración/explotación
RESULTADOS EXPERIMENTALES (50 ciudades, óptimo = 1000):
Método Costo Promedio Desviación Tiempo
Random (10k) 1350 ±150 5 seg
Hill Climbing 1180 ±80 1 seg
Simulated Annealing 1049 ±25 10 seg
Branch & Bound 1000 0 2 horasLección: SA encuentra soluciones mucho mejores que greedy/random, en tiempo razonable, sin quedar atrapado en óptimos locales.
¿Por qué Simulated Annealing acepta soluciones peores ocasionalmente?
6. Parámetros de Simulated Annealing
Configuración Crítica
El éxito de SA depende del ajuste adecuado de sus parámetros.
Parámetros Clave
"""
CONFIGURACIÓN DE SIMULATED ANNEALING
1. TEMPERATURA INICIAL (T₀):
Objetivo: Permitir exploración amplia inicial
Heurística: Aceptar ~80-90% de empeoramientos al inicio
Cálculo: T₀ = -Δ_promedio / ln(P_inicial)
donde:
- Δ_promedio = cambio promedio de costo
- P_inicial = probabilidad inicial deseada (0.8-0.9)
Típico: T₀ = 1.0 a 100.0 (normalizar costos)
2. TEMPERATURA FINAL (T_min):
Objetivo: Criterio de parada
Típico: T_min = 0.001 × T₀
O: temperatura donde P(aceptar peor) < 0.01
3. FACTOR DE ENFRIAMIENTO (α):
Objetivo: Controlar velocidad de descenso
Fórmula: T\_{i+1} = α × T_i
Típico: α = 0.85 - 0.99
- α = 0.85: enfriamiento rápido (menos iteraciones)
- α = 0.95: enfriamiento lento (más exploración)
- α = 0.99: muy lento (mejor calidad, más tiempo)
Trade-off: Calidad vs Tiempo
4. ITERACIONES POR TEMPERATURA:
Objetivo: Alcanzar equilibrio térmico
Heurística: Proporcional al tamaño del problema
Típico:
- Mínimo: 100 iteraciones
- Ideal: n × log(n) donde n = tamaño
- TSP-50: 200-500 iteraciones
- TSP-100: 500-1000 iteraciones
5. ESQUEMAS DE ENFRIAMIENTO:
a) GEOMÉTRICO (más común):
T\_{i+1} = α × T_i
b) LINEAL:
T\_{i+1} = T_i - δ
c) LOGARÍTMICO (teóricamente óptimo, muy lento):
T_i = T₀ / log(1 + i)
d) EXPONENCIAL:
T_i = T₀ × e^(-i/τ)
EJEMPLO DE CONFIGURACIÓN:
# Para TSP pequeño (n ≤ 20):
T_initial = 10.0
T_min = 0.01
alpha = 0.95
iter_per_temp = 100
# Para TSP mediano (20 < n ≤ 100):
T_initial = 100.0
T_min = 0.001
alpha = 0.97
iter_per_temp = n \* 10
# Para TSP grande (n > 100):
T_initial = 1000.0
T_min = 0.0001
alpha = 0.99
iter_per_temp = n \* 5
"""
7. Otros Problemas NP-Completos
Aplicabilidad Universal
Las estrategias (aproximación y heurísticas) se aplican a TODOS los problemas NP-completos.
Estrategias por Problema Específico
Aproximaciones y heurísticas por problema
TSP (Traveling Salesman):
- Aproximación: 2-aprox (TSP métrico), 1.5-aprox (Christofides)
- Heurísticas: Simulated Annealing, Algoritmos genéticos, Lin-Kernighan
Knapsack (0/1 Mochila):
- Aproximación: FPTAS: -aproximación
- Heurísticas: Greedy por densidad, DP (pseudo-poli)
Vertex Cover:
- Aproximación: 2-aproximación (matching)
- Heurísticas: Greedy por grado, LP rounding
Graph Coloring:
- Aproximación: -aproximación
- Heurísticas: Greedy (Welsh-Powell), Backtracking, SA
Set Cover:
- Aproximación: -aproximación (greedy)
- Heurísticas: Greedy por costo-efectividad, LP rounding
Bin Packing:
- Aproximación: First Fit (1.7-aprox), First Fit Decreasing (1.22-aprox)
- Heurísticas: Best Fit, Next Fit
Job Scheduling:
- Aproximación: List scheduling (2-aproximación)
- Heurísticas: Shortest Job First, Simulated Annealing
8. Computación Cuántica y Límites
Mitos y Realidad
Mito: Las computadoras cuánticas resolverán problemas NP-completos en tiempo polinomial.
Realidad: NO se espera que las computadoras cuánticas resuelvan NP-completos en tiempo polinomial.
Lo que SÍ pueden hacer:
- Algoritmo de Shor: Factorización en tiempo polinomial
- Algoritmo de Grover: Búsqueda con speedup cuadrático
- Simulación cuántica
Implicación para NP: Mejora de a (Grover), sigue siendo exponencial.
Factorización y Criptografía
Impacto cuántico en seguridad
Problema Clásico:
- Factorizar (p, q primos grandes)
- Mejor algoritmo clásico: Exponencial en dígitos
- Ejemplo: n de 2048 bits → años de cómputo
Algoritmo de Shor (Cuántico):
- Factorizar n en tiempo polinomial:
- Ejemplo: n de 2048 bits → minutos (con computadora cuántica)
Impacto en RSA:
RSA depende de dureza de factorización. Con computadora cuántica suficientemente grande:
- ✗ RSA inseguro
- ✗ Firmas digitales comprometidas
- ✗ HTTPS vulnerable
Solución: Criptografía post-cuántica
- ✓ Lattice-based cryptography
- ✓ Code-based cryptography
- ✓ Hash-based signatures
Estado Actual (2025):
- Computadoras cuánticas: ~100 qubits
- Necesario para romper RSA-2048: ~4000 qubits
- Aún lejos de amenaza práctica
- Investigación activa en post-cuántica
Generación de Claves:
El muestreo aleatorio funciona bien:
- Generar números primos grandes aleatorios
- Verificar primalidad (Miller-Rabin)
- Construir clave pública (n, e)
- Construir clave privada (d)
Eficiente incluso con computadoras clásicas.
9. Resumen de Estrategias
Guía de Selección
¿Qué método usar para tu problema NP-completo?
Instancia pequeña (n ≤ 20): → Búsqueda exhaustiva con poda (Branch and Bound) → Programación dinámica exponencial
Necesitas GARANTÍA de calidad: → Algoritmo de aproximación (si existe) → Factor constante del óptimo
Necesitas VELOCIDAD, sin garantía formal: → Heurística greedy → Backtracking con buenas podas
Instancia grande, necesitas CALIDAD: → Simulated Annealing → Algoritmos genéticos → Tabu Search
Tienes ESTRUCTURA ESPECIAL: → Explotar propiedades (planaridad, árboles, intervalos) → Puede haber algoritmo polinomial para subclase
Casos PROMEDIO son fáciles: → Algoritmo exacto con podas inteligentes → Funciona bien en práctica
Tabla Comparativa Final
| Método | Tiempo | Calidad | Garantía | Cuándo Usar |
|---|---|---|---|---|
| Exacto | Exponencial | Óptima | ✅ 100% | n ≤ 20 |
| Aproximación | Polinomial | α × óptimo | ✅ Factor α | Necesitas garantía |
| Greedy | Rápido | Variable | ❌ Ninguna | Primera aproximación |
| Backtracking | Exponencial | Óptima | ✅ 100% | Caso promedio rápido |
| SA/Genético | Ajustable | Muy buena | ❌ Ninguna | Balance calidad/tiempo |
10. Cierre: Lecciones Prácticas
Conclusiones Clave
1. NP-Completo NO Significa Imposible
Significa: no hay algoritmo polinomial exacto (probablemente). Pero hay muchas alternativas prácticas.
2. Múltiples Herramientas Disponibles
- Aproximación: garantías demostrables
- Heurísticas: soluciones rápidas y buenas
- Casos especiales: explotar estructura
3. Ajustar Expectativas
No busques perfección, busca solución útil:
- 5% sobre óptimo suele ser suficiente
- Tiempo razonable > perfección inalcanzable
4. Modelado Cuidadoso
Éxito de heurísticas depende de:
- Buena representación de estado
- Función de costo adecuada
- Función de vecindad efectiva
5. Experimentación es Clave
Ajustar parámetros (SA, genéticos) requiere prueba y error en tus instancias específicas.
Para un problema NP-completo con n=100, ¿qué método es más apropiado si necesitas alta calidad sin garantía formal?
Resumen Final
Has Dominado la Gestión de Intratabilidad
✓ Realidad de NP-Completos:
- Demostrar dureza no es el final
- Problemas reales requieren soluciones prácticas
- Tres vías: caso promedio, heurísticas, aproximación
✓ Algoritmos de Aproximación:
- Garantía de factor α del óptimo
- Válida para TODAS las instancias
- Ejemplo: 2-aproximación para Vertex Cover
✓ Simulated Annealing:
- Heurística robusta y versátil
- Escapa óptimos locales
- Balance exploración/explotación
- Requiere ajuste de parámetros
✓ Estrategia de Selección:
- n pequeño → exacto con poda
- Necesitas garantía → aproximación
- Instancia grande + calidad → SA
- Estructura especial → explotar
✓ Límites Fundamentales:
- Computación cuántica NO resuelve NP-completos en poli
- Pero puede impactar criptografía (Shor)
- Buscar soluciones “suficientemente buenas”
¡Felicitaciones!
Has completado la gestión de intratabilidad. Ahora dominas:
- Por qué NP-completitud no es el final del camino
- Algoritmos de aproximación con garantías
- Simulated Annealing: teoría y práctica
- Configuración de parámetros para SA
- Estrategias para cada tipo de problema
- Limitaciones de computación cuántica
- Cuándo usar cada método
- Balance entre calidad, tiempo y garantías
Próximo paso: Explorar algoritmos genéticos, tabu search, optimización de colonia de hormigas, y técnicas metaheurísticas avanzadas. También: teoría de aproximación (PTAS, FPTAS) y límites de aproximabilidad.