Cómo reconocer un problema como un problema de programación dinámica

Esta pregunta es un poco engañosa, porque supone que algunos problemas son “problemas de programación dinámica” y otros no. En teoría, podría usar la programación dinámica para resolver cualquier problema. La verdadera pregunta es si quieres o no.

La programación dinámica es una de las muchas herramientas algorítmicas que debería tener disponibles en su caja de herramientas algorítmicas. Desde mi experiencia personal, es la herramienta más útil para resolver problemas que están en el límite de ser manejables e intratables .

Los problemas manejables son aquellos que pueden resolverse con un algoritmo de tiempo polinómico. Los problemas intratables son aquellos que parecen resolverse solo con algoritmos que se ejecutan en tiempo de ejecución exponencial o peor. Por ejemplo, se cree que toda la clase de problemas NP-completos es intratable (aunque nadie puede probarlo).

Lo primero que debe hacer cuando se le presenta un problema con el que no está familiarizado es pensar primero cuál sería la solución de fuerza bruta. Por lo general, la solución de fuerza bruta tendrá un tiempo de ejecución horrible, probablemente un tiempo de ejecución factorial.

La siguiente pregunta que debe hacerse es si el problema es obviamente manejable (es decir, si existe una solución obviamente correcta que sea mucho más eficiente que la fuerza bruta).

Como un ejemplo trivial, considere ordenar. La solución de la fuerza bruta sería considerar todos los ordenamientos posibles y elegir el que está en el orden correcto. Este es tiempo factorial y un algoritmo muy muy horrible. Pero hay un algoritmo obvio que es mucho más eficiente que eso, Selección Ordenar (es decir, encontrar el elemento más pequeño, luego el siguiente más pequeño, etc.) Por lo tanto, la clasificación es obviamente manejable. ¿Es Selection Sort el algoritmo más eficiente? No, pero dado que el problema obviamente ya es muy manejable, no recurriría a DP para encontrar un algoritmo mejor.

Ahora, consideremos algunos problemas como el Vendedor ambulante, la Mochila y la Subsecuencia creciente más larga, jugar al Tic-Tac-Toe, etc. Las soluciones de fuerza bruta a estos problemas deberían ser tan obvias como para la clasificación, y nuevamente son un tiempo factorial realmente malo algoritmos La diferencia es que con estos problemas, después de pensar en ellos por un período de tiempo, aún no es obvio que no son intratables. Parece que hay una explosión combinatoria natural en el problema que es difícil de evitar.

Aquí es cuando es hora de sacar la Programación Dinámica de su caja de herramientas, antes de darse por vencido y asumir que el algoritmo de fuerza bruta es el mejor. Resulta que en cada uno de los problemas que mencioné anteriormente, DP le dará una mejor solución que la fuerza bruta. Aunque, eso no siempre significa que el problema sea manejable. Por ejemplo, para TSP, aunque la programación dinámica se puede utilizar para reducir el tiempo de ejecución de factorial a exponencial, todavía es intratable (y se presume que siempre lo será, ya que TSP es NP-Complete). Para Knapsack, DP le ofrece un mejor algoritmo, pero solo es un pseudopolinomio.

El uso de la programación dinámica tampoco garantiza que obtendrá el algoritmo con el mejor tiempo de ejecución. Por ejemplo, usando DP, no es difícil obtener un tiempo de ejecución de O (n ^ 2) para el problema de Subsecuencia de Mayor Incremento, pero obtener el algoritmo de O (n log n) requiere un montón de trucos adicionales. Del mismo modo, por ejemplo, el problema de la ruta más corta, podría usar DP para obtener una solución razonable, pero no sería tan bueno como simplemente usar Dijkstra.

Además, el simple hecho de saber que debe resolver un problema con DP no le dice realmente cómo hacerlo. A menudo, es posible que deba reformular el problema de varias maneras antes de ver cómo DP le dará una buena solución. Por lo tanto, realmente necesita estudiar un montón de problemas que se resuelven utilizando la Programación dinámica para tener una idea de ellos, y pensar en diferentes formas en que estos problemas podrían reformularse. Los problemas de tipo mochila pueden disfrazarse de miles de millones de maneras diferentes.

Algo más a considerar es que lo bueno de los algoritmos DP es que, por lo general, su prueba de corrección es evidente. Otras estrategias algorítmicas son a menudo mucho más difíciles de probar correctas, y por lo tanto más propensas a errores. Por ejemplo, los algoritmos codiciosos pueden parecer conceptualmente más simples y, por lo general, se ejecutan más rápido, pero es mucho más difícil demostrar que son correctos porque generalmente requieren hacer muchas suposiciones implícitas sobre la estructura de su entrada. Y la exactitud de esos supuestos implícitos es la diferencia entre un algoritmo que solo funciona algunas veces y uno que funciona todo el tiempo.

La programación dinámica es útil para resolver problemas de optimización , por lo que a menudo, la mejor manera de reconocer un problema como solucionable mediante programación dinámica es reconocer que un problema es un problema de optimización .

Por ejemplo, mira los problemas aquí:

Problemas de práctica de programación dinámica

Observe cuántos de los problemas son problemas de optimización. Con problemas de optimización, verá términos como más corto / más largo , minimizado / maximizado , menos / más , menos / más grande , más grande / más pequeño , etc.

Cuando vea este tipo de términos, el problema puede pedir un número específico (como ” encontrar el número mínimo de operaciones de edición “) o puede pedir un resultado (como ” encontrar la subsecuencia común más larga “). El último tipo de problema es más difícil de reconocer como un problema de programación dinámica, por lo que debe prestar atención a cualquier cosa que parezca optimización.

Cuando tenga un problema de optimización, primero identifique para qué está optimizando. Una vez que se da cuenta de lo que está optimizando, debe decidir qué tan fácil es realizar esa optimización. A veces, el enfoque codicioso es suficiente para una solución óptima. Si el problema parece bastante difícil y tendría problemas para encontrar la respuesta por su cuenta sin una computadora o cálculo, entonces la programación dinámica es probablemente un buen candidato.

La programación dinámica simplemente adopta el enfoque de la fuerza bruta, identifica el trabajo repetido y elimina la repetición. Entonces, incluso antes de comenzar a formular el problema como un problema de programación dinámica, piense en cómo podría ser la solución de fuerza bruta. ¿Podría posiblemente haber subpasos repetidos en la solución de fuerza bruta? Si es así, intente formular su problema como un problema de programación dinámica.

Para formular el problema como un problema de programación dinámica, debe asegurarse de configurarlo correctamente, o tal vez no piense que la programación dinámica puede ayudarlo. Asegúrese de poder identificar el parámetro para el que está optimizando. Luego intente identificar las entradas. Enumere todas las entradas que afectan la respuesta y preocúpese por reducir el tamaño de ese conjunto más adelante. Una vez que haya identificado las entradas y salidas, intente identificar si el problema puede dividirse en subproblemas más pequeños. Si puede identificar subproblemas más pequeños, entonces probablemente pueda aplicar programación dinámica para resolver el problema. Luego, averigua cuál es tu recurrencia y resuélvela. Cuando intente averiguar su recurrencia, recuerde que cualquier recurrencia que escriba tiene que ayudarlo a encontrar su respuesta. A veces, la respuesta será el resultado de la recurrencia, y a veces tendrá que derivar el resultado observando algunos resultados de la recurrencia.

Cada paso de la recurrencia puede tener un mal tiempo de ejecución, por lo que su primer intento de una solución de programación dinámica puede ser lento. Está bien. Una vez que resuelva el problema con la programación dinámica, la optimización será fácil. Por ejemplo, puede considerar formas de preprocesar sus datos para acelerar cada paso de la recurrencia. Puede pensar en nuevas formas de ordenar sus subproblemas cambiando cuáles son sus entradas (diferentes tipos de entradas pueden representar los mismos datos). Puede intentar combinar su recurrencia consigo mismo para reducir la cantidad de parámetros de entrada que necesita (por ejemplo, la cantidad de widgets producidos desde el tiempo i al tiempo j es la cantidad de widgets producidos en el tiempo j menos la cantidad de widgets producidos en el tiempo i , para que pueda hacer que su recurrencia use una variable, hora , en lugar de hora de inicio y hora de finalización ).

Nota: el hecho de que un problema pueda resolverse con programación dinámica no significa que no exista una solución más eficiente. Resolver un problema con la programación dinámica parece mágico, pero recuerda que la programación dinámica es simplemente una fuerza bruta inteligente. A veces vale la pena, y a veces ayuda solo un poco.

La respuesta breve es que si ha practicado suficientes problemas de programación dinámica, generalmente puede ser bastante obvio en primer lugar. Cuando hago preguntas de programación dinámica en entrevistas, algunos buenos candidatos pueden decirme de inmediato que quieren probar un enfoque de programación dinámica.

Supongo que esto no es lo que quieres escuchar, pero para ser honesto, no importa lo que la gente “mágica” te diga aquí, aún debes practicar una serie de preguntas por ti mismo.

Definitivamente, hay otras formas de reconocer las preguntas de programación dinámica. Una es que cuando te das cuenta de que puedes dividir el gran problema en subproblemas, esto podría ser un problema de programación dinámica. En otras palabras, si se pueden usar soluciones a subproblemas para resolver el problema general, al menos puede intentar la recursividad. Para acelerarlo, el almacenamiento de resultados temporales en la memoria es uno de los enfoques más comunes.

Además, se recomienda resolver primero un problema con el enfoque más ingenuo. Muchas veces podemos probar la fuerza bruta con varios bucles for. Si notas que los bucles no funcionarán porque terminarás con bucles infinitos, probablemente esto indique programación dinámica.

Una vez más, no hay garantía de que algunos patrones conduzcan a una programación dinámica. Aún necesitas practicar tanto como puedas. Aquí hay un par de preguntas (con análisis):

  • Suma máxima de elementos no adyacentes
  • Subarreglo con suma dada
  • Una guía paso a paso para la programación dinámica

Bueno, descubrí que las personas tienen muchos problemas para comprender la programación dinámica. Por lo tanto, he escrito una publicación de blog sobre lo mismo para explicarlo de la mejor manera posible y en un lenguaje simple. Haga clic aquí para ver la publicación del blog. Puede plantear su preocupación en los comentarios si tiene dudas después de leer el tutorial.

Se satisfacen los subproblemas superpuestos y las propiedades óptimas de la subestructura.

More Interesting

¿Recomendaría usar HackerRank para mejorar las habilidades del algoritmo? ¿Por qué?

¿Podría alguien ayudarme con el problema del algoritmo 'Intervalo casi ordenado'?

¿Cuáles son los algoritmos gráficos 'imprescindibles' para un programador competitivo?

¿Cuáles son los mejores recursos para aprender R? Tratando de construir mi propio algoritmo de predicción basado en datos anteriores que tengo en archivos csv y que solía ser un desarrollador de Ruby hace un par de años

¿Qué debo hacer para autoaprendizaje de ciencias de la computación con interés en inteligencia artificial y ciencias de la computación teóricas?

Cómo implementar un algoritmo usando la recursividad para encontrar el módulo de esta serie

¿Hay algún algoritmo que sea más rápido que log (n)?

¿Cuál es el mejor algoritmo para encontrar números primos? ¿Cuál es su sintaxis?

¿Qué debo hacer después de aprender Python? ¿Programación competitiva o aprender Djanjo o aprender algoritmos y estructura de datos en Python?

¿Es Pegasos un buen algoritmo para SVM no lineal?

¿Cuáles son las ventajas de un árbol de búsqueda binaria sobre un árbol rojo-negro?

¿En qué tipo de índices de búsqueda y enfoque se debe trabajar para un sitio web con búsqueda basada en la ciudad (y localidad) de una palabra clave, un ejemplo típico es un directorio web?

¿Cómo dirigen los sistemas de guía del vehículo de lanzamiento la carga útil hacia órbitas tan precisas?

¿Cuáles son los mejores algoritmos de Real Space Renormalization Group?

¿Quién sabe qué hay detrás de la API de Google Nearby Search? ¿Qué algoritmo usan? ¿Cómo encuentra Google una estación de servicio cercana?