¿Se puede automatizar el análisis algorítmico?

8

¿Alguien ha pensado en la posibilidad de un lenguaje de programación y un compilador, de modo que el compilador pueda hacer automáticamente el análisis asintótico del peor de los casos? El caso de uso que tengo en mente es un lenguaje de programación donde escribo código y compilo. El compilador me dice que mi código se ejecuta en O (n ^ 2) (por ejemplo). Hace esto haciendo lo que hacen las personas inteligentes que hacen análisis algorítmico, posiblemente contando bucles, etc.

  • Debido a la detención de problemas problemáticos, y dado que uno puede tener programas que funcionan al combinarse, por ejemplo, el algoritmo de Levin para SAT que se ejecuta en tiempo polinomial si P = NP, sospecho que uno debe diseñar el lenguaje de programación para que sea lo suficientemente restrictivo como para permitir algo como esto. ¿Hay resultados negativos que descartan que ciertos tipos de lenguajes de programación tengan tales compiladores?

  • También me interesarían los sistemas que no ofrecen un análisis asintótico exacto, sino un límite superior "interesante".

  • Específicamente NO estoy interesado en el cuadro negro y los métodos estadísticos que toman muestras de entradas de una longitud particular, y descubro cuánto tiempo lleva el programa. Estos métodos son muy interesantes, pero no son lo que estoy buscando. Estoy interesado en métodos exactos que pueden dar límites aproximados .

Estaría muy agradecido si alguien pudiera señalarme algunas referencias sobre el trabajo en esta dirección.

manoj
fuente
55
Este es un posible duplicado de la siguiente pregunta: cstheory.stackexchange.com/questions/12585/…
cody
Los lenguajes obvios que se pueden descartar son Turing completo, ya que el problema indeciso de detención se encuentra en dicho lenguaje. entonces esto lleva a la pregunta, ¿qué es un lenguaje más limitado que Turing completo para el cual esto es posible? pero podría decirse que un lenguaje no es un "lenguaje de programación" a menos que esté completo. por lo tanto, aproximadamente, tal meta es básicamente teóricamente imposible ... sin embargo, puede haber algunas ideas atenuantes para lenguajes de complejidad limitada ...
vzn
La respuesta es no, no podemos distinguir algorítmicamente incluso entre el caso de que el programa se ejecute en el tiempo y . Consulte ¿Los límites de tiempo de ejecución en P son decidibles? (suponiendo que el lenguaje de programación es lo suficientemente expresivo, es decir, podemos simular otro programa dado para un número dado de pasos). n 3n2n3
Kaveh
3
@Kaveh No estoy de acuerdo con que mi pregunta sea un duplicado de "¿Son decidibles los límites de tiempo de ejecución en P?". Ya sabía cómo demostrar que eso es imposible. Sin embargo, ¡gracias por traer este hilo también a mi atención! Sin embargo, es posible que mi pregunta sea una pregunta duplicada, como lo señala Cody.
manoj
2
Cuando era joven e ingenuo intenté escribir un analizador de complejidad usando gcov . Te da la cantidad de veces que se ejecutó cada línea. :) ¡Buena pregunta!
Pratik Deoghare

Respuestas:

8

La complejidad implícita nos ha enseñado que (algunas) clases de complejidad pueden clasificarse por sistemas de tipos, en el sentido de que hay sistemas de tipos que solo aceptan programas polinomiales, por ejemplo. Una ramificación más práctica de esta investigación es RAML (Resource Aware ML), un lenguaje de programación funcional con un sistema de tipos que le dará información precisa sobre los tiempos de ejecución de sus programas. Es más preciso, de hecho, que la complejidad big-O, también se incluyen factores constantes, parametrizados por un modelo de costo para las operaciones base.

Puede pensar que esto es trampa: seguramente algunos algoritmos tienen una complejidad que es muy difícil de calcular con precisión, entonces, ¿cómo podría este lenguaje determinar fácilmente la complejidad de sus programas? El truco es que hay muchas formas de expresar un algoritmo dado, algunas que el sistema de tipos rechazará (rechaza algunos programas excelentes) y otras, tal vez, que acepta. Por lo tanto, el usuario no obtiene el mundo de forma gratuita: ya no tiene que calcular las estimaciones de costos, sino que debe descubrir cómo expresar el código de una manera que sea aceptada por el sistema.

(Siempre es el mismo truco, como ocurre con los lenguajes de programación con solo cálculos finales o propiedades de seguridad comprobables, etc.)

gasche
fuente
Gracias, esta es una respuesta muy interesante. Echaré un vistazo más de cerca a RAML.
manoj
7

La herramienta COSTA desarrollada por el grupo de investigación COSTA hace exactamente lo que desea. Dado un programa (en formato de código de bytes Java), la herramienta produce una ecuación de costos basada en un modelo de costos proporcionado por el programador. El modelo de costos puede involucrar entidades como tiempo de ejecución, uso de memoria o eventos facturables (como el envío de mensajes de texto). La ecuación de tiempo de ejecución se resuelve usando una herramienta dedicada para producir una forma cerrada, el límite superior del caso más desfavorable del modelo de costos.

Naturalmente, la herramienta no funciona en todos los casos, ya sea al no producir una ecuación de tiempo de ejecución o al no encontrar una forma cerrada. Esto no es sorprendente.

Dave Clarke
fuente
2

De hecho, pensé en la misma pregunta hace un tiempo. Aquí está el tren de pensamiento que tenía:

Como dijiste, el problema de detención es un problema. Para evitar esto, necesitamos un lenguaje que solo permita programas que siempre se detengan. Por otro lado, nuestro lenguaje debe ser lo suficientemente expresivo como para tratar los problemas más comunes (por ejemplo, al menos debe capturar toda la clase de complejidad EXP).

Entonces, echemos un vistazo a los programas LOOP. Un programa LOOP siempre se detiene y su expresividad va mucho más allá de EXP, para tener una mejor idea: puede simular cualquier TM para la que la función de tiempo de ejecución se puede expresar como programa LOOP.

Ahora, podemos ver la profundidad de anidamiento y la magnitud del número de repeticiones para cada ciclo desde un punto de vista sintáctico y tenemos un punto de partida (aunque no sabemos qué tan lejos nos lleva). Sin embargo, considere el siguiente problema:

Queremos implementar la función f(n)

tal que y es el primer número primo después de nf(n)=yyny>n

Supongamos que tenemos una función que nos dice si un número n es primo o no. Luego de implementarp(n)nf(n)

y := n + 1
while not prime(y) 
    y++
return y

Ahora, ¿cómo vamos a hacer esto solo con bucles limitados? El problema ahora es pensar en un límite superior para el rango de números que tenemos que buscar.

¡Pero eso era lo que queríamos que nuestro compilador nos dijera en cierto sentido! Entonces, simplemente cambiamos el problema de reflexionar sobre la complejidad del tiempo al programador. Por cierto, la siguiente oración nos da el límite superior de nuestro problema:

 prime pa prime p:p<pp!+1

Mi intuición aquí es que la única información que un compilador puede brindarle es analizar cuidadosamente la sintaxis que finalmente proporciona el programador. Entonces, permitir que el compilador haga una declaración significativa sobre la complejidad temporal de un programa significa forzar al programador a incorporar esta información en el programa.

Sin embargo, el último párrafo es subjetivo y ciertamente podría haber otros enfoques posibles que arrojen resultados interesantes. Solo quería dar una idea de lo que no se debe esperar al recorrer este camino.

John D.
fuente
2
La profundidad de anidamiento de los bucles es el problema principal aquí, y la complejidad varía sobre las funciones recursivas primitivas, ver La complejidad de los programas de bucles , Meyer & Ritchie, ACM '67 Conference, dx.doi.org/10.1145/800196.806014 .
Sylvain
Gracias. Creo que haces un buen punto aquí. Hay un problema si voy a ejecutar bucles del tipo mientras (predicate = true) hago algo porque entonces tengo que discutir sobre la primera vez que el predicado se vuelve falso. Para reformular mi pregunta dentro del contexto que ha establecido, me pregunto si uno puede diseñar un montón de primitivas, con un tiempo de ejecución bien definido, que se pueda componer de maneras simples para escribir programas interesantes y obtener límites de tiempo de ejecución para ellos . Quizás RAML ya hace algo de esto, como lo señala la primera respuesta.
manoj
1
Para dar más sugerencias de este tipo, la dificultad computacional intrínseca de las funciones de Cobham se cita a menudo como uno de los primeros trabajos sobre la complejidad implícita: caracteriza la FP por medio de esquemas de recursión. Encontrará un trabajo más reciente en esta línea de Bellatoni & Cook en Una nueva caracterización teórica de la recursión de las funciones polytime , Computational Complexity 1992. Vea también una encuesta de Hofmann en SIGACT News 31 (1): 31--42, 2000: dx.doi.org/10.1145/346048.346051 .
Sylvain
Gracias Sylvain, estoy echando un vistazo a la encuesta! He sido consciente de esta área de la teoría de la complejidad implícita, pero nunca me he molestado realmente en estudiarla, debido a la falta de apreciación de cuál es la pregunta básica que están tratando de responder. ¡Ahora tengo más motivación para entender estas ideas!
manoj