¿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.
Respuestas:
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.)
fuente
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.
fuente
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ónF( n )
tal que y es el primer número primo después de nF( n ) = y y norte y> n
Supongamos que tenemos una función que nos dice si un número n es primo o no. Luego de implementarp ( n ) norte F( n )
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:
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.
fuente