Nos concentramos en problemas de decisión ya que permite uniformizar el estudio para todas las versiones de problemas.
Definiciones:
-
$\Pi$ : problema. -
$D_\Pi$ : el conjunto de todas las instancias de$\Pi$ . -
$I \in D_\Pi$ : instancia de un$\Pi$ . -
$Y_\Pi$ : conjunto de todas las instancias$I$ tales que $\Pi(I) = $ sí.
Una máquina de Turing es una cinta infinita la cual se mueve hacia la izquierda o derecha pasando por un cabezal. Además, en todo momento la máquina tiene un estado interno. En cada paso, el cabezal lee el símbolo en esa posición de la cinta y determina cuál es el nuevo estado, hacia dónde mover y qué otro símbolo escribir en la cinta.
En la versión determinística (MTD), para cada combinación de estado y símbolo hay una única opción para hacer.
En cambio, la versión no determinística (MTND) puede tener muchas opciones para realizar para la misma combinación de estado y símbolo leído. No está especificado de antemano ningún criterio para elegir qué hacer. Podemos pensarlo como que se hacen todo al mismo tiempo. Obviamente no existe (aún) una máquina capaz de computar de esta forma.
P: polinomiales. Existe una MTD polinomial que resuelve el problema de decisión. Es equivalente a decir que existe un algoritmo en la máquina RAM que resuelve el problema en tiempo polinomial.
NP: polinomiales no-determinísticos. Existe una MTND polinomial que para toda instancia del problema de decisión donde la respuesta es sí, la MTND responde sí. Es equivalente a decir que para una instancia del problema donde la respuesta es sí, existe un certificado de tamaño polinomial (respecto a la instancia) que garantiza que la respuesta es sí, y además existe un algoritmo que puede verificar la garantía del certificado en tiempo polinomial (respecto al certificado).
Una reducción o transformación polinomial de un problema
Si existe la transformación
Las reducciones polinomiales son transitivas:
Un problema
-
$\Pi \in$ NP: podemos verificar un certificado en tiempo polinomial. - Para todo problema
$\Pi' \in$ NP,$\Pi' \leq_P \Pi$ : el problema$\Pi$ es al menos tan difícil como cualquier otro problema de NP. Decimos que$\Pi$ es NP-Hard.
Si encontramos una forma de resolver un problema NP-completo en tiempo polinomial, entonces todos los problemas en NP podrían resolverse también en tiempo polinomial.
La clase Co-NP son los problemas
Dado un problema
Más formalmente:
-
$D_{\Pi^c} = D_\Pi$ : mismas instancias. -
$Y_{\Pi^c} = D_\Pi - Y_\Pi$ : responde al revés.
Si
En cambio, si
Lo que sí podemos decir es que si
SAT es NP-Completo.
Para demostrarlo, por un lado sabemos que SAT es NP porque una valuación concreta de la fórmula es en si misma un certificado, y podemos verificar que la valuación efectivamente satisface la fórmula en tiempo polinomial.
Por otro lado tenemos que mostrar que todos los otros problemas
A partir del resultado de Cook-Levin tenemos una estrategia más fácil para demostrar que un problema es NP-Completo. Se basa en utilizar la transitividad de las reducciones polinomiales.
Si queremos demostrar que un problema de decisión
- Mostramos que
$\Pi \in$ NP. Para esto presentamos un certificado polinomial y su respectivo verificador polinomial. - Encontramos otro problema
$\Pi'$ que ya sabemos que es NP-Completo y mostramos que existe una reducción polinomial tal que$\Pi' \leq_P \Pi$ .
En la práctica determinar si el problema que estamos resolviendo pertenece a NP-Completo nos permite focalizar nuestros esfuerzos de manera inteligente. Ya sabemos que el problema es difícil y no se conocen algoritmos eficientes para resolverlo. No obstante, todavía podemos obtener resultados aceptables empleando algunas de las siguientes estrategias.
- Analizar si el problema tiene restricciones que lo simplifiquen y así poder encontrar un algoritmo eficiente.
- Analizar si el problema admite una solución pseudo-polinomial.
- Analizar si el problema se puede reducir a algún otro problema NP-Completo que tenga solvers eficientes en la práctica.
- Analizar si el tamaño de nuestras instancias admite una solución basada en fuerza bruta o backtracking.
- Buscar heurísticas aprovechando las particularidades de nuestro problema.
- Analizar si existen algoritmos aproximados que nos brinden una solución aceptable para nuestras necesidades.