¿Cuántos pares de paréntesis son suficientes para completar Brainfuck Turing?


12

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


1
"¿Cuántos pares de estos brackets deben usarse?" ¿Puede aclarar "tiene que ser utilizado". Por ejemplo, ¿qué pasa si le pido a BrainF que cuente hasta 21000000 ?
John L.

@ Apass.Jack el número mínimo de paréntesis
MilkyWay90

1
Oh, que quería decir el número mínimo de soportes para simular un máquina de Turing -state como una función de n ? De todos modos, ¿puedes dar un ejemplo no trivial que sea lo más simple posible? nn
John L.

1
@ Apass.Jack Ok, estoy creando un programa BF con errores que funciona para una máquina de Turing de un solo estado
MilkyWay90

@ Apass.Jack Nevermind, es demasiado difícil para mí. Básicamente, hago un intérprete BF para mi lenguaje de programación, Turing Machine But Way Worse , cuando usa solo dos símbolos posibles (0 y 1) y elimina completamente el aspecto de E / S y de detención
MilkyWay90

Respuestas:


9

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:

  1. Todos los contadores tienen el mismo valor de restablecimiento automático R ; es decir, el mapa de disparo TWM f tiene la propiedad de que f(x,x)=R para todas las x .
  2. Hay un solo contador de detención h .
  3. El número c de contadores es (p1)/2 para algún número primo p .

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).

Sea r una raíz primitiva de p=2c+1 . Definir la función

g(k)=4ck((rk1)mod(2c+1)),k=0,,2c1.

  1. g es una regla Golomb de orden . Es decir, la diferencia es única para cada par de números distintos .2 c g ( i ) - g ( j ) i , j { 0 , , 2 c - 1 }2cg(i)g(j)i,j{0,,2c1}
  2. g(k)mod(2c) toma cada valor exactamente una vez.0,,2c1

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,,c1}u ( x ) v ( x ) u(x) v(x)

u(x)=g(k1)<v(x)=g(k2) with u(x)v(x)x(modc)

Por la segunda propiedad de hay exactamente dos valores distintos de para elegir.gk1,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.02R

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)NcN+1v((x+1)modc)u(x)x

inicialización ajustes[ >×(H+cN+1) [ <×c × H] <×H ]

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(c1)

  1. Las celdas de valor se inicializan al doble del contenido inicial del contador TWM ​​correspondiente, excepto que el contador se decrementa previamente.0
  2. Las celdas de reserva se establecen en , excepto la celda , que se establece en .0u(c1)2R
  3. Todas las otras celdas accesibles por el programa (un número finito) se establecen en .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(c1)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)Hv(x)Hx

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 ]cyv(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:

  1. Ajuste la celda de retroceso del contador anterior en .u(x)2R
  2. Ajuste la celda de reserva del contador actual en , excepto si la celda activa actual es y, por lo tanto, deberíamos detenernos.u(y)2Rv(h)
  3. Ajuste la celda de valor del siguiente contador en (decrementando el contador).v((y+1)modc)2
  4. Cuando la celda activa es una celda de valor (de modo que el contador ha alcanzado cero), ajuste todas las celdas de valor en desde el mapa de disparo TWM. se ajusta por .v(y)yv(z)2f(y,z)v(y)2R

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.2R0

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.<×HH

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)]


12

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 decirfxf(x,y)yf(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.xxf(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á).ppqp(1+s+s2)sp2q2p2q2pp

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 inclusive0i<p22i2q2p+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í:

inicialización ajuste[>>>[ >×(2p1) [ <×(2p) ]>-] <<<]

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 de2122q01so 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 justa -; 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 izquierda2p12x2p+2x12p2x1x2p2x+12p 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ódulo2p2x+12p2q112p2log2,p(r)+112r1rlog2,pp2paumenta 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).2p2pp

Cuando 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)yxxy

  • La diferencia entre dos potencias de 2 es un número binario que consiste en una cadena de 1 o más s seguida de una cadena de 0 o más s (con los valores de posición del inicio del número y el inicio de la cadena , dependiendo del mayor y menor respectivamente de e ); así, todas esas diferencias son distintas. * En cuanto a la diferencia de una potencia de 2 y , debe contener al menos dos transiciones entre cadenas de sy s (100xyq10q contiene al menos cuatro transiciones de este tipo, la resta solo puede eliminar 2), por lo tanto, es distinta de todas las diferencias de dos poderes de dos, y estas diferencias obviamente también son distintas entre sí.

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=yf(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.


¡Buen trabajo! ¡Veo que trabajaste en esto en TNB!
MilkyWay90

Creo que necesitas s para ser al menos p + 2. Cuando s = p + 1, q es 1 menos que una potencia de 2.
Ørjan Johansen

Creo que he encontrado una mucho más simple (como en requiriendo ningún primer teoría de números) la colocación de contador: 2p*2^i+2i.
Ørjan Johansen el

@ ØrjanJohansen: Correcto, creo que mencioné esa construcción en #esoteric (algún tiempo después de que escribí esta publicación)? Todo lo que realmente necesita es una regla de Golomb para la cual cada elemento tenga un módulo distinto al número de elementos, y hay varias formas de construirlos (aunque encontrar los óptimos es difícil; el más largo que he encontrado (a través de la fuerza bruta) es [0, 1, 3, 7, 20, 32, 42, 53, 58]para p = 9).
ais523

Oh, así lo hiciste (justo antes de decir que mi cerebro se negaba a estar en modo matemático, así que ahí está mi excusa: P). Supongo que descubrí que k = 0 era suficiente, entonces. Creo que la construcción Erdős – Turan_ de Wikipedia da un polinomio en crecimiento (y presumiblemente O () - ¿óptimo?) Uno si usa solo la primera mitad de los elementos (la otra mitad se repite (mod p)).
Ørjan Johansen el
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.