Brainfuck es un lenguaje de programación completo de Turing que usa solo 8 símbolos (6 si ignora las E / S).
Los dos más notables que lo empujan a la integridad de Turing son [
y ]
, esencialmente, la etiqueta y el goto de Brainfuck.
Normalmente, los programas en Brainfuck usan múltiples conjuntos de []
, pero me preguntaba exactamente cuántos pares de estos corchetes deben usarse para completar Brainfuck Turing.
Más simplemente, ¿cuál es la menor cantidad de paréntesis que necesitaría para simular una máquina de Turing de estado n (proporcione el número de paréntesis para máquinas de Turing de 1, 2 y tres estados)?
Notas:
Asumimos una cinta infinita y sin límites computacionales.
Es una máquina de Turing de 2 símbolos
turing-completeness
MilkyWay90
fuente
fuente
Respuestas:
Este es un desarrollo adicional de la respuesta de @ ais523 , reduciéndolo a solo dos conjuntos de paréntesis, y también usando una colocación de celda más compacta basada en la teoría de la regla de Golomb. ais523 ha creado un compilador para esta construcción , así como esta sesión de TIO que muestra un ejemplo del programa BF resultante que se ejecuta con el seguimiento de depuración de los contadores TWM.
Al igual que el original, esto comienza con un programa en The Waterfall Model , con algunas restricciones que no pierden generalidad:
Gobernante Golomb
Combinamos la construcción Erdős – Turán con la función de permutación de una matriz Welch – Costas para obtener una regla Golomb con las propiedades necesarias.
(Estoy seguro de que esta construcción combinada no puede ser una idea nueva, pero acabamos de encontrar y encajar estas dos piezas de Wikipedia).
Sear una raíz primitiva de p=2c+1 . Definir la función
Estructura de la cinta
Para cada contador TWM , asignamos dos posiciones de celda de cinta BF, una celda de reserva y una celda de valor :x∈{0,…,c−1} u ( x ) v ( x ) u(x) v(x)
Por la segunda propiedad de hay exactamente dos valores distintos de para elegir.g k1,k2
El contenido de una celda de reserva se mantendrá la mayor parte del tiempo en , excepto cuando se haya visitado su contador, cuando esté en , el doble del valor de restablecimiento automático del contador. Una celda de valor se mantendrá al doble del valor del contador TWM correspondiente.0 2R
Todas las demás celdas a las que puede llegar la ejecución del programa BF (un número finito) se mantendrán en valores impares, de modo que siempre se prueben como distintos de cero. Después de la inicialización, esto es automático porque todos los ajustes de celda son por cantidades pares.
Si lo desea, todas las posiciones de las celdas se pueden desplazar hacia la derecha por una constante para evitar moverse a la izquierda de la posición inicial de la cinta BF.
Estructura del programa BF
Sea la distancia entre el valor del contador de detención y las celdas de reserva, y sea un número lo suficientemente grande como para que para todos los contadores . Entonces la estructura básica del programa BF esH=v(h)−u(h) N cN+1≥v((x+1)modc)−u(x) x
Inicialización
La fase de inicialización establece todas las celdas accesibles por el programa a sus valores iniciales, en un estado como si el último contador hubiera sido visitado y la celda activa era su celda de respaldo :u(c−1)
Luego, el puntero de la cinta se mueve a la posición (una celda siempre distinta de cero) antes de llegar primero al programa .u(c−1)−H
[
Comienzo del bucle externo
Al comienzo de una iteración del bucle externo, el puntero de la cinta estará en o para un contador .u(x)−H v(x)−H x
Deje ser el siguiente contador para visitar.y=((x+1)modc)
El movimiento coloca el puntero de la cinta en una posición que es y no a la izquierda de .×(H+cN+1) ≡y(modc) v(y)
>
El bucle interno ahora busca hacia la izquierda en los pasos de para una celda cero. Si el contador es cero, se detendrá en la celda de valor (cero) ; de lo contrario, encontrará la celda de reserva .×c c y v(y) u(y)
[
<
]
Cualquier celda que se encuentre se convierte en la nueva celda activa .
Ajustes
La fase de ajuste ajusta varias celdas en la cinta según su posición con respecto a la celda activa. Esta sección contiene solo
+-><
comandos y, por lo tanto, estos ajustes ocurren incondicionalmente. Sin embargo, debido a que todas las celdas relacionadas con el contador están en un patrón de regla de Golomb, cualquier ajuste que no sea apropiado para la celda activa actual perderá todas las celdas importantes y en su lugar ajustará alguna celda irrelevante (mientras la mantiene impar).Por lo tanto, debe incluirse un código separado en el programa para cada posible par requerido de celda activa y ajustada, excepto para el autoajuste de una celda activa, que, debido a que el ajuste se basa únicamente en la posición relativa, debe compartirse entre todos ellos.
Los ajustes requeridos son:
El primer y segundo ajustes anteriormente se hacen necesarias por el hecho de que todas las células activas deben ajustarse a sí mismos por el mismo valor, que es para celdas de valores, y por lo tanto también para las células de retorno. Esto requiere preparar y limpiar las celdas de reserva para garantizar que vuelvan a tanto en el valor como en las ramas de reserva.2R 0
Fin del bucle externo
El movimiento representa que al final de la fase de ajuste, el puntero de la cinta se mueve a la izquierda de la celda activa.×H H
<
Para todas las celdas activas que no sean la celda de valor del contador de detención , esta es una celda irrelevante, y por lo tanto impar y distinta de cero, y el bucle externo continúa durante otra iteración.v(h)
Para , el puntero se coloca en su celda de reserva , para lo cual hemos hecho una excepción arriba para mantenerlo en cero, y así el programa sale por el final y se detiene.v(h) u(h)
]
fuente
No estoy 100% seguro de que sea imposible hacer esto con dos conjuntos de paréntesis. Sin embargo, si las celdas de la cinta BF permiten valores ilimitados, tres conjuntos de corchetes son suficientes. (Por simplicidad, también supondré que podemos mover el cabezal de la cinta hacia la izquierda más allá de su punto de partida, aunque debido a que esta construcción solo usa una región finita de la cinta, podríamos levantar esa restricción agregando suficientes
>
comandos al comienzo del programa.) La construcción a continuación requiere asumir la conjetura de Artinpara poder compilar programas arbitrariamente grandes; sin embargo, incluso si la conjetura de Artin es falsa, aún será posible mostrar la integridad de Turing indirectamente mediante la traducción de un intérprete de un lenguaje completo de Turing a BF usando la construcción a continuación, y ejecutando programas arbitrarios al darlos como entrada a ese intérprete.El lenguaje completo de Turing que estamos compilando en BF ilimitado es The Waterfall Model , que es uno de los modelos computacionales más simples conocidos. Para las personas que aún no lo saben, consta de varios contadores (y valores iniciales para ellos), y una función desde pares de contadores hasta enteros; la ejecución del programa consiste en restar repetidamente 1 de cada contador, luego, si algún contador es 0, agregar a cada contador (el programa se escribe de tal manera que esto nunca suceda en múltiples contadores simultáneamente). Hay una prueba de integridad de Turing para este lenguaje detrás de mi enlace. Sin pérdida de generalidad, asumiremos que todos los contadores tienen el mismo valor de reinicio automático (es decirf x f(x,y) y f(x,x) es igual para todas las ); esta es una suposición segura porque para cualquier específico , agregar la misma constante a cada no cambiará el comportamiento del programa.x x f(x,y)
Sea el número de contadores; sin pérdida de generalidad (suponiendo que la conjetura de Artin), se supone que tiene una primitiva raíz 2. Sea sea , donde es la menor potencia de 2 mayor que . Sin pérdida de generalidad, será menor que ( está limitado polinomialmente, crece exponencialmente, por lo que cualquier suficientemente grande funcionará).p p q p(1+s+s2) s p 2q 2p 2q 2p p
La disposición de la cinta es la siguiente: numeramos cada contador con un número entero (y sin pérdida de generalidad, suponemos que hay un solo contador de detención y lo numeramos ). El valor de la mayoría de los contadores se almacena en la celda de cinta , con la excepción del contador 0, que se almacena en la celda de cinta . Para cada celda de cinta con números impares desde la celda -1 hasta inclusive0≤i<p 2 2i 2q 2p+1+2p+1 , esa celda de cinta siempre contiene 1, a menos que esté inmediatamente a la izquierda de un contador, en cuyo caso siempre contiene 0. Las celdas de cinta de números pares que no se utilizan como contadores tienen valores irrelevantes (que podrían ser 0 o no) ); y celdas de cinta con números impares fuera del rango establecido también tienen valores irrelevantes. Tenga en cuenta que configurar la cinta en un estado inicial apropiado requiere inicializar solo finitamente muchos elementos de cinta a valores constantes, lo que significa que podemos hacerlo con una secuencia de
<>+-
instrucciones (de hecho, solo>+
son necesarias), por lo tanto, no hay corchetes. Al final de esta inicialización, movemos el puntero de la cinta a la celda -1.La forma general de nuestro programa se verá así:
La inicialización pone la cinta en la forma esperada y el puntero en la celda -1. Esta no es la celda a la izquierda de un contador (0 no es una potencia de 2), por lo que tiene el valor 1 y entramos en el bucle. El bucle invariante para este bucle más externo es que el puntero de la cinta es (al comienzo y al final de cada iteración del bucle) tres celdas a la izquierda de un contador; se puede ver que el bucle solo saldrá si estamos tres celdas a la izquierda del contador 2 (cada contador tiene un 1 tres celdas a su izquierda, ya que tener un 0 implicaría que las posiciones de la cinta de dos contadores estaban separadas por 2 celdas; las únicas dos potencias de 2 que difieren en 2 son y , y la representación binaria de cambia de cadenas de sa cadenas de21 22 q 0 1 so viceversa al menos cuatro veces y, por lo tanto, no puede estar a 1 de una potencia de 2).
El segundo bucle repetidamente recorre los contadores, decrementándolos. El bucle invariante es que el puntero de la cinta siempre apunta a un contador; así el ciclo saldrá cuando algún contador se convierta en 0. La disminución es justa2p−1 2x 2p+2x−1 2p 2x−1 x 2p 2x+1 2p espacios, que tampoco cambian el índice de la celda de cinta módulo , y finalmente deben encontrar la celda congruente a módulo que tiene el valor (que será la celda a la izquierda de algún contador); debido a nuestro requisito de raíz primitiva, hay exactamente una de esas celdas ( es congruente con módulo , y es congruente con para cualquier otro , donde es el logaritmo discreto para la base 2 módulo ). Además, se puede ver que la posición del puntero de cinta módulo2p 2x+1 2p 2q−1 −1 2p 2log2,p(r)+1−1 2r−1 r log2,p p 2p aumenta en cada vez alrededor del ciclo medio. Por lo tanto, el puntero de la cinta debe alternar entre todos los contadores (en orden de sus valores, módulo ). Por lo tanto, cada iteraciones, disminuimos cada contador (según sea necesario). Si el bucle se rompe a la mitad de una iteración, reanudaremos la disminución cuando volvamos a ingresar al bucle (porque el resto del bucle más externo no hace ningún cambio neto en la posición del puntero de la cinta).2 p 2p p
-
; La forma en que pasamos de un mostrador al siguiente es más compleja. La idea básica es que mover espacios a la derecha desde nos colocará en una celda con números impares , que está a la derecha de cualquier contador ( es el último contador, es positivo porque es positivo); módulo , este valor es congruente con (por el pequeño teorema de Fermat) . El bucle más interno se mueve repetidamente hacia la izquierdaCuando un contador llega a 0, el bucle intermedio se rompe, llevándonos al código de "ajuste". Esto es básicamente una codificación de ; para cada par , se añade al elemento de cinta que es la misma distancia a la izquierda / derecha del puntero actual de la cinta como contador 's posición de la cinta está a la izquierda / derecha de contador ' s ubicación de la cinta (y luego quita el puntero de la cinta de nuevo a donde comenzó). Cada vez que , esta distancia resulta ser única:f (x,y) f(x,y) y x x≠y
Para , obviamente encontramos que la distancia movida es 0. Pero debido a que todas son iguales, podemos usar esto como el ajuste para la celda actual. Y se puede ver que el código de ajuste implementa el efecto "cuando un contador llega a 0" para cada contador; todas las celdas que realmente representan contadores se ajustarán en la cantidad correcta, y todos los demás ajustes afectarán a las celdas pares sin contador (la diferencia entre dos números pares es par), que no tienen efecto en el comportamiento del programa.x=y f(x,y)
Por lo tanto, ahora tenemos una compilación funcional de cualquier programa en The Waterfall Model para BF (incluido el comportamiento de detención, pero sin incluir E / S, que no es necesaria para completar Turing) usando solo tres pares de paréntesis y, por lo tanto, tres pares de paréntesis son suficientes para completar Turing.
fuente
2p*2^i+2i
.[0, 1, 3, 7, 20, 32, 42, 53, 58]
para p = 9).