¿Cuál es la mejor manera de saber qué estrategia (codiciosa, dividir y conquistar, programación dinámica) funcionaría mejor para un problema de algoritmo en particular?

Respuesta corta: prueba todo.

Respuesta más larga: esta es una habilidad que solo viene con experiencia. Hasta que tenga esa experiencia, será difícil, la única buena manera de aprender es probar muchas cosas y fracasar en la mayoría de ellas, pero hay algunas estrategias que pueden ayudar.

Primero, olvídate de los algoritmos codiciosos hasta que tengas un algoritmo de programación dinámico que funcione. Es increíblemente fácil crear estrategias ambiciosas que parezcan razonables pero que no resuelvan el problema por completo. Literalmente les digo a mis alumnos de algoritmos que escribir las letras g, r, e, e, d, y en ese orden es una instrucción inequívoca de su subconsciente para usar la programación dinámica.

Entre las opciones que enumera, la única opción que importa es dividir y conquistar versus programación dinámica. En esencia, ambas son estrategias recursivas. En ambos casos, su trabajo es transformar la instancia dada del problema en una o más instancias más simples del mismo problema, que luego será resuelto por el Hada mágica de recursión. Por ejemplo:

* Búsqueda binaria: utilice una comparación para determinar si el valor objetivo es la mitad superior o la mitad inferior de la matriz ordenada, y luego deje que el Hada de recursión busque a través de la mitad correcta.

* Fusionar dos listas ordenadas: use una comparación para determinar el primer elemento en la lista de salida, y luego deje que el Hada de recursión combine el resto.

* Clasificación: deje que el Hada de la recursión clasifique la mitad superior y la mitad inferior de la matriz de entrada, y luego combine las dos mitades clasificadas (como se indicó anteriormente).

* Subsecuencia común más larga: hay tres opciones:
1. Descarta el primer personaje de A y deja que el Hada de la recursión encuentre la subsecuencia común más larga de lo que queda
2. Descarta el primer personaje de B, deja que el Hada de la recursión encuentre la subsecuencia común más larga de lo que queda
3. Acepte los primeros caracteres iguales de A y B como el primer carácter de la subsecuencia común más larga, y deje que el Hada de la Recursión descubra el resto.
Pruébelos todos e informe el mejor de esos tres resultados.

La principal diferencia entre las dos técnicas es que el enfoque de divide y vencerás produce subproblemas recursivos que son significativamente más pequeños (de n a n / 2 o n / 3 o 3n / 4, por ejemplo), mientras que el enfoque de programación dinámica produce recursivos subproblemas que son solo un poco más pequeños (típicamente de n a n-1 o n-2). Otra diferencia es que los algoritmos de divide y vencerás tienden a ser más sencillos (partición, recurrencia y limpieza), mientras que los algoritmos de programación dinámica (implícitamente) implican retroceso: intente esto, luego intente eso, luego intente lo otro y finalmente devuelva lo que sea El resultado fue el mejor.

El verdadero cuello de botella al aplicar cualquiera de estas estrategias es que hay que entender la recursividad . En particular, tienes que creer, realmente creer, que la recursión realmente funciona. Esas instancias más pequeñas son el problema de alguien más. Esas instancias más pequeñas no son asunto suyo. Si comienza a rastrear llamadas recursivas en lugar de confiar en que se resolverán correctamente, es muy probable que se pierda y se confunda. La hada de la recursión es tu amiga. Confía en el hada de la recursión. Si no.

Gracias por la pregunta! Es mi tipo favorito de tareas que doy durante un curso introductorio a la programación.

Respuesta corta:
Pruébalos en el siguiente orden.

  1. Prueba la programación codiciosa, y si fallas,
  2. Prueba la programación dinámica, y si fallas,
  3. Intente retroceder, pero considere varias mejoras, incluida la memorización.

Algunos detalles más:
Estas técnicas forman una especie de jerarquía. Programación codiciosa
brinda las soluciones más rápidas pero tiene la menor aplicabilidad. El retroceso generalmente explota al costo de tiempo exponencial, pero tiene la mayor aplicabilidad.
La programación dinámica está en ambos aspectos en el medio.

  1. Usando programación codiciosa, la clave es asegurarse de que su idea de solución sea correcta. Por lo general, debe verificar dos propiedades:
    – al hacer una elección codiciosa, debe existir una solución óptima que contenga esta opción,
    – Después de hacer la elección codiciosa, el problema se reduce a uno más pequeño, y la solución óptima al problema más pequeño más su elección dan una solución óptima al problema original.
  2. En la programación dinámica, enriquece el problema original al introducir algunos parámetros, identificar dependencias entre soluciones a problemas parametrizados y luego calcular soluciones a todos en un orden adecuado. La parametrización del problema se puede hacer de muchas maneras. Intente encontrar dichos parámetros, que la respuesta a cada instancia del problema se pueda calcular de manera eficiente, pero el espacio de soluciones que tiene que calcular no es demasiado grande. A veces también puede ahorrar memoria al no almacenar todas las soluciones que se calculan (por ejemplo, dos filas de una matriz en lugar de la matriz completa).
  3. Cuando utilice el seguimiento, busque siempre posibles mejoras. Incluso si su solución sigue siendo exponencial, la reducción de la base del exponente tiene un gran impacto en el rendimiento:
    – Si las llamadas recursivas con los mismos parámetros se repiten varias veces, agregue la memorización. A veces, puede dar resultados comparables a la programación dinámica.
    – Si la solución que construyes es una colección de algunos elementos, sigue insertándolos en un orden fijo (por ejemplo, en el problema de 8 reinas, coloca las reinas en las filas consecutivas del tablero de ajedrez).
    – Pode las ramas de recursión: si hay algunos criterios obvios que pueden permitirle omitir algunas llamadas recursivas, aplíquelas.
    – Considere el orden en el que realiza las llamadas recursivas. Elija un orden que pueda llevarlo a la solución más rápido, o podará más llamadas recursivas.

Consulte estos materiales de capacitación de Codility para practicar con programación codiciosa y dinámica:
https://codility.com/programmers
https://codility.com/programmers
Y aquí está mi rompecabezas favorito sobre qué técnica debería aplicar:
https://codility.com/programmers
Puede leer el spoiler vinculado allí después de elegir cómo resolverlo.

More Interesting

¿Cuál es la relación entre las cadenas de Markov y los procesos de Poisson?

¿Por qué la complejidad temporal del siguiente código O (logn)?

La mayoría de las definiciones / teoremas / ejemplos de privacidad diferencial que he encontrado son para consultas que devuelven un solo número por columna, como un promedio. ¿Existen mecanismos diferencialmente privados para otros tipos de consultas, como los que subconjustan filas en función de algún criterio?

¿Qué es un código de clasificación?

En F (n) -F (n-1) = n ^ 8, ¿qué es F (n)?

¿Qué harías? ¿Cuál hubiera sido tu estrategia si hubieras tenido la oportunidad de volver a comenzar la programación de aprendizaje?

¿Qué métodos matemáticos se utilizan para rastrear el efecto de mercado de los algoritmos comerciales de alta frecuencia?

¿Qué algoritmos de minería de datos puedo usar para maximizar las ganancias de una compañía de tarjetas de regalo que almacena ventas, pedidos y datos de clientes en una base de datos relacional?

¿Qué es segmentar segmentado?

¿Cómo funcionan los algoritmos y la estructura de datos cuando procesamos cualquier solicitud en un sitio web?

En la programación de computadoras, ¿es cierto que una recursión funcionará mejor para encontrar todas las rutas posibles que los bucles anidados?

Cómo representar el algoritmo de hash SHA256 en python

En el algoritmo O (n) para encontrar el elemento máximo en una matriz, ¿cuál es el valor esperado del número total de cambios en el valor de una variable que mantiene el máximo sobre el paso de una matriz?

¿Cuál es el algoritmo más complicado por el que has pasado?

¿Cómo podemos dividir un conjunto dado de números (posiblemente negativos) en dos partes que tienen el mismo promedio?