¿Es posible resolver el problema de detención si tiene una entrada restringida o predecible?

18

El problema de la detención no se puede resolver en el caso general. Es posible proponer reglas definidas que restrinjan las entradas permitidas y ¿se puede resolver el problema de detención para ese caso especial?

Por ejemplo, parece probable que un lenguaje que no permita bucles, por ejemplo, sea muy fácil de determinar si el programa se detendrá o no.

El problema que estoy tratando de resolver en este momento es que estoy tratando de hacer un verificador de script que verifique la validez del programa. Se puede resolver el problema de detención si sé exactamente qué esperar de los escritores de guiones, lo que significa entradas muy predecibles. Si esto no se puede resolver exactamente, ¿cuáles son algunas buenas técnicas de aproximación para resolver esto?

Ken Li
fuente

Respuestas:

10

La respuesta intuitiva es que si no tiene bucles ilimitados y no tiene recursividad y no tiene goto, sus programas terminan. Esto no es del todo cierto, hay otras formas de infiltrar la no terminación, pero es lo suficientemente bueno para la mayoría de los casos prácticos. Por supuesto, lo contrario es incorrecto, hay lenguajes con estas construcciones que no permiten programas sin terminación, pero utilizan otros tipos de restricciones, como sistemas de tipos sofisticados.

Recursividad

Una restricción común en los lenguajes de scripting es prevenir dinámicamente la recursividad: si A llama a B llama a C llama ... llama a A, entonces el intérprete (o el verificador, en su caso) se da por vencido o señala un error, incluso si la recursión realmente podría terminar. Dos ejemplos concretos:

  • El preprocesador C deja una macro intacta mientras está expandiendo esa macro. El uso más común es definir un contenedor alrededor de una función:

    #define f(x) (printf("calling f(%d)\n", (x)), f(x))
    f(3);
    

    Esto se expande a

    (printf("calling f(%d)\n", (3)), f(3))
    

    La recursividad mutua también se maneja. Una consecuencia es que el preprocesador C siempre termina, aunque es posible construir macros con alta complejidad de tiempo de ejecución.

    #define f0(x) x(x)x(x)
    #define f1(x) f0(f0(x))
    #define f2(x) f1(f1(x))
    #define f3(x) f2(f2(x))
    f3(x)
    
  • Los shells de Unix expanden los alias de forma recursiva, pero solo hasta que encuentran un alias que ya se está expandiendo. Nuevamente, el propósito principal es definir un alias para un comando con un nombre similar.

    alias ls='ls --color'
    alias ll='ls -l'
    

nn

Existen técnicas más generales para demostrar que las llamadas recursivas terminan, como encontrar un número entero positivo que siempre disminuye de una llamada recursiva a la siguiente, pero son mucho más difíciles de detectar. A menudo son difíciles de verificar, y mucho menos inferir.

Bucles

formn

En particular, con bucles for (más construcciones de lenguaje razonables como condicionales), puede escribir todas las funciones recursivas primitivas , y viceversa. Puede reconocer sintácticamente las funciones recursivas primitivas (si están escritas de manera no ofuscada), porque no usan el bucle while o goto o la recursión u otro truco. Se garantiza que las funciones recursivas primitivas terminarán, y la mayoría de las tareas prácticas no van más allá de la recursividad primitiva.

Gilles 'SO- deja de ser malvado'
fuente
4

Ver Terminator y APROVe . Tienden a depender de la heurística, y no estoy seguro de si describen claramente la clase de programas para los que trabajan. Aún así, se consideran de vanguardia, por lo que deberían ser buenos puntos de partida para usted.

rgrig
fuente
4

Si, puede ser posible. Una forma común de resolver tales problemas es considerar un parámetro no controlable adicional (monótono) que depende del código como parte de la entrada. La complejidad del problema que tiene ese parámetro se puede reducir severamente.

No podemos calcular el parámetro, pero si sabe que las instancias de entrada con las que está tratando tienen valores de parámetro pequeños, puede fijarlo en un número pequeño y usar el algoritmo.

Este y otros trucos similares se usan en métodos formales para tratar la indecidibilidad de la detención y problemas similares. Pero si lo que desea decidir es complicado, es poco probable que la complejidad de sus algoritmos sea mejor que ejecutar el algoritmo en esas instancias.

Con respecto a la otra pregunta, si restringe las entradas lo suficiente, entonces el problema de detención puede ser fácil. Por ejemplo, si sabe que las entradas son algoritmos de tiempo polinomiales, decidir el problema de detención para ellos es trivial (ya que cada algoritmo de tiempo polinomial se detiene).

Los problemas que surgen en los métodos formales son generalmente indecidibles, puede consultar la literatura sobre cómo tratan estos problemas en la práctica.

Kaveh
fuente
4

No es una respuesta formalmente rígida, pero aquí va:

El problema para determinar si se detiene o se repite para siempre. Looping en colecciones finitas de un elemento a la vez o entre un intervalo de números está bien. EDITAR: Obviamente, esto solo funcionará si la colección iterada o el intervalo tienen prohibido cambiar (por ejemplo, por inmutabilidad) cuando se está iterando (o al menos, prohibido crecer).

La recursión probablemente no esté bien, a menos que establezca una regla artificial para que sea finita, como permitir una profundidad de pila máxima o forzar que un parámetro no negativo disminuya en cada iteración.

Los gotos arbitrarios son generalmente malos. Es muy probable que los gotos hacia atrás conduzcan a bucles que pueden ser infinitos.

Las declaraciones de whiles y do-whiles son un problema, porque dependen de una condición que no se garantiza que cambie o no durante la ejecución. Una forma posible (pero probablemente muy insatisfactoria) de restringir eso es dar un número máximo de iteraciones posibles.

Victor Stafusa
fuente
2

Debe proporcionar una definición de su lenguaje de script, y qué quiere decir con "esperar" de los escritores de script.

O(nω)

Hay un resultado similar para una clase de programa polinomial de Aaron R. Bradley, Zohar Manna y Henny B. Sipma. Pero AFAIK (podría estar equivocado aquí) el tiempo de ejecución es doblemente exponencial (esencialmente el tiempo requerido para calcular una base de Groebner).


fuente