Análisis de complejidad de algoritmos en implementaciones de lenguaje de programación funcional

10

Hoy he aprendido que el análisis de algoritmos difiere según el modelo computacional. Es algo en lo que nunca he pensado ni oído hablar.

Un ejemplo que me dio, que lo ilustró aún más, por el usuario @chi fue:

Por ejemplo, considere la tarea: dado devuelve . En RAM esto se puede resolver en ya que el acceso a la matriz es de tiempo constante. Usando TMs, necesitamos escanear toda la entrada, por lo que esx i O ( 1 ) O ( n )(i,x1,,xn)xiO(1)O(n)

Esto me hace preguntarme acerca de los lenguajes funcionales; Según tengo entendido, "los lenguajes funcionales están íntimamente relacionados con el cálculo lambda" (de un comentario de Yuval Filmus aquí ). Entonces, si los lenguajes funcionales se basan en el cálculo lambda, pero se ejecutan en máquinas basadas en RAM, ¿cuál es la forma correcta de realizar análisis de complejidad en algoritmos implementados usando estructuras de datos e idiomas puramente funcionales?

No he tenido la oportunidad de leer Estructuras de datos puramente funcionales, pero he examinado el tema en la página de Wikipedia, y parece que algunas de las estructuras de datos reemplazan a las matrices tradicionales con:

"Las matrices se pueden reemplazar por un mapa o una lista de acceso aleatorio, que admite una implementación puramente funcional, pero el tiempo de acceso y actualización es logarítmico".

En ese caso, el modelo computacional sería diferente, ¿correcto?

Abdul
fuente
3
Definitivamente no soy un experto en este tema, pero creo que escuché que 1) una máquina similar a un lisp (con su propio modelo de costo) puede simular programas RAM con un factor adicional (esto parece fácil de probar) y 2) si este factor es realmente necesario sigue siendo un problema abierto. Además, se puede argumentar que asignar un costo O (1) al acceso a la matriz en el modelo RAM es demasiado generoso. En hardware, el acceso a la memoria tiene que atravesar puertas O ( log n ) donde n es el tamaño de la memoria física. O(Iniciar sesiónnorte)O(Iniciar sesiónnorte)norte
chi
1
También tenga en cuenta que prácticamente todos los lenguajes FP del mundo real tienen matrices de alguna forma, con un tiempo de acceso garantizado (como en los idiomas imperativos). Esto generalmente se resuelve agregándolos como un lenguaje primitivo. O(1)
chi
1
Un ejemplo de un modelo computacional diferente sería el número de reducciones beta realizadas en un término de cálculo lambda. En FP estamos más usando un modelo de carnero disfrazado de cálculo lambda, si eso tiene sentido
Kurt Mueller
1
@KurtMueller Tenga en cuenta que podemos obtener un término lambda de tamaño después de solo reducciones de O ( n ) bete. Esto hace que el modelo de costo de contar el número de beta sea poco realista. Podría decirse que uno mejor podría ser sopesar cada paso por el tamaño de los términos en cuestión. Sin embargo, este no es el único modelo posible: la evaluación óptima de los términos lambda no aplica beta de la manera ingenua, prefiriendo algunas máquinas de reducción de gráficos más sofisticadas. En tal caso, contar las betas probablemente no sería apropiado. O(2norte)O(norte)
chi
1
Tenga en cuenta que también necesita saber si su lenguaje funcional es ansioso o vago / estricto o no estricto. Recientemente encontré una situación en la que un algoritmo del mundo real era polinomial en Haskell (no estricto) pero la traducción ingenua a OCaml (estricto) era exponencial.
Eric Lippert

Respuestas:

6

Depende de la semántica de su lenguaje funcional. No puede hacer el análisis de algoritmos en lenguajes de programación de forma aislada, porque no sabe lo que realmente significan las declaraciones. La especificación para su idioma debe proporcionar una semántica suficientemente detallada. Si su idioma especifica todo en términos de cálculo lambda, necesita alguna medida de costo para las reducciones (¿son O (1) o dependen del tamaño del término que reduce?).

Creo que la mayoría de los lenguajes funcionales no lo hacen de esa manera y en su lugar proporcionan declaraciones más útiles como "las llamadas a funciones son O (1), añadiendo al encabezado de una lista es O (1)", cosas así.

adrianN
fuente
Creo que entiendo su respuesta (el malentendido se debe probablemente a mi falta de comprensión en el cálculo lambda): Usted dice que básicamente tiene que hacer el análisis caso por caso (caso sea lenguaje), en lugar de un de manera general, porque ciertas operaciones tienen diferentes significados por idioma. ¿Es correcto mi entendimiento?
Abdul
Si. Su diseñador de idiomas necesita decirle qué significan realmente las cosas que puede escribir en el idioma antes de poder analizar el tiempo de ejecución de un algoritmo.
adrianN
"No se puede hacer un análisis de algoritmos en lenguajes de programación de forma aislada", ¿se refería a lenguajes FP o lenguajes en general? Si se refería a lo anterior, entonces, ¿cómo analizamos los algoritmos en la escuela de manera tan general, es decir, analizamos los problemas de Java, C / C ++ y Python? ¿Es porque todos son muy similares? ¿O es porque las estructuras de datos subyacentes y los ADT son todos iguales e implementados de la misma manera también? O, por último, ¿es porque estos cursos son simplemente por el bien de la educación y no necesariamente tienen que ser estrictamente precisos?
Abdul
1
Es cierto para todos los lenguajes de programación. Para ser estrictamente correcto, primero debe reparar un modelo de máquina, digamos la RAM y (un pequeño puñado) de instrucciones que admite. Solo puede hacer un análisis de los programas utilizando solo esas instrucciones. Entonces puede pensar en un mapeo de su lenguaje de programación a ese modelo de máquina. Luego puede analizar programas en el lenguaje de programación. Para un tratamiento muy riguroso, compruebe cómo lo hace Knuth en The Art of Computer Programming. Mucho de esto se puede simplificar debido a las constantes de ocultación big-O.
adrianN