Secuencia Collatz en una máquina de dos contadores

8

La secuencia de Collatz a partir de un entero positivo n se define de esta manera:

  • si n es par, divídalo por 2 ( n' = n / 2)
  • si n es impar, multiplíquelo por 3 y agregue 1 ( n' = 3n + 1)

Repita la iteración anterior hasta que n alcance 1.

No se sabe (es un problema importante no resuelto en teoría de números) si la secuencia finalmente alcanzará el número 1, independientemente de qué número entero positivo se elija inicialmente.

Una máquina de dos contadores (2CM) es una máquina equipada con dos registros que pueden contener un valor entero no negativo y pueden programarse con el siguiente conjunto de instrucciones:

INCX    increase the value of register X
INCY    increase the value of register Y
JMP n   jump to instruction n
DJZX n  if register X is zero jump to instruction n,
        otherwise decrement its value
DJZY n  if register Y is zero jump to instruction n,
        otherwise decrement its value
HALT    halt (and accept)
PRINTX  print the content of register X

Un programa de 2CM es simplemente una secuencia de instrucciones, por ejemplo, el siguiente programa simplemente copia el contenido del registro X para registrar Y:

cp:   DJZX end
      INCY
      JMP cp
end:  HALT

Tenga en cuenta que un 2CM es Turing completo (es decir, puede calcular todas las funciones computables con una codificación de entrada adecuada, pero aquí es irrelevante). También tenga en cuenta que el conjunto de instrucciones es un poco diferente al del artículo de Wikipedia.

El reto

Escriba el programa 2CM más corto, que calcule e imprima la secuencia de collatz hasta 1 y se detenga (el registro X inicialmente contiene el valor inicial ny el registro Y inicialmente contiene 0). Tenga en cuenta que la longitud de un programa de 2CM es la cantidad de instrucciones utilizadas (no la longitud del texto).

Por ejemplo, cuando se inicia desde X = 3, debe imprimir: 3 10 5 16 8 4 2 1y HALT.

Por lo tanto, puede usar su idioma favorito para construir un simulador / intérprete de 2CM, pero el código final (más corto) que ingrese en la respuesta debe estar en el idioma de 2CM .

Marzio De Biasi
fuente
¿Qué programa escribiremos para la máquina de 2 cm?
FUZxxl
¿Su programa tiene que terminar en HALT o también puede dejar que la ejecución fluya fuera del final?
orlp
1
@ LegionMammal978 No importa el tamaño del código.
FUZxxl
3
@MarzioDeBiasi Al ver todos estos comentarios, permítanme recomendar el sandbox (al menos para su próximo desafío). Escribir desafíos claros es difícil, e incluso si cree que lo ha resuelto todo, a menudo hay preguntas abiertas para otros usuarios, que se pueden señalar en el sandbox y abordar antes de publicar el desafío en main y la gente comienza a trabajar en eso.
Martin Ender

Respuestas:

11

18 instrucciones

Estaba un poco decepcionado de haber llegado tarde a la escena, ya que la naturaleza minimalista del problema y el lenguaje hacen que (aparentemente) solo haya un enfoque general para una buena respuesta. Recibí una respuesta de 19 instrucciones con bastante rapidez, pero no sentí que trajera lo suficiente a la mesa como para publicarla. Pero después de mucho rascarme la cabeza, mi experiencia de ensamblaje hacky z80 llegó y encontré una manera de guardar una instrucción reutilizando un bloque de código para un propósito para el que no estaba destinado.

# Let N be the previous number in the Collatz sequence.

# Print N, and if N==1, halt.
# X=N, Y=0
Main:           PRINTX          # Print N.
                DJZX Done       # X=N-1 (N shouldn't be zero, so this never jumps)
                DJZX Done       # If N-1==0, halt. Otherwise, X=N-2.

# Find the parity of N and jump to the proper code to generate the next N.
# X=N-2, Y=0
FindParity:     INCY
                DJZX EvenNext   # If N%2==0, go to EvenNext with X=0, Y=N-1.
                INCY
                DJZX OddNext    # If N%2==1, go to OddNext with X=0, Y=N-1.
                JMP FindParity

# Find the next N, given that the previous N is even.
# X=0, Y=N-1
EvenNext:       INCX
                DJZY Main       # Y=Y-1 (Y should be odd, so this never jumps)
                DJZY Main       # If Y==0, go to Main with X=(Y+1)/2=N/2, Y=0.
                JMP EvenNext

# Find the next N, given that the previous N is odd.
# X=0, Y=N-1
OddNext:        INCX
                INCX
                INCX
                DJZY EvenNext   # If Y==0, go to EvenNext with X=(Y+1)*3=N*3, Y=0.
                JMP OddNext     # ^ Abuses EvenNext to do the final INCX so X=N*3+1.

# Halt.
Done:           HALT
Runer112
fuente
1
Espero que mi intérprete no haya sido tan malo: P Buena solución.
orlp
1
@orlp Funcionó como un encanto. Gracias. :)
Runer112
1
¡Realmente me gusta tu solución! Muy buen abuso de EvenNext :)
Nejc
4

PUNTUACIÓN: 21

Aquí está mi intento:

main: imprime Xy salta a finish(si X==1).

divisibility: hace una distinción si X%2==0o X%2==1. También copia Xa Yy hace X==0. Salta a isDivisible(if X%2==0) o isNotDivisible(if X%2==1).

isDivisible: bucle utilizado cuando Y%2==0. Por cada disminución de Y2, aumenta Xen 1. Cuando Y==0, salta a main.

isNotDivisible: utilizado cuando Y%2==1. Aumenta Xen 1.

notDivLoop: bucle utilizado cuando Y%2==1. Por cada disminución de Y1, aumenta Xen 3. Cuando Y==0, salta a main.

finish: se detiene

main:           PRINTX              # print X
                DJZX main           # here X is always >0 and jump never fires (it is just for decreasing)
                DJZX finish         # if initially X==1 this jumps to finish
                INCX                # establish the previous state of X
                INCX
                                    # continue with X>1

divisibility:   DJZX isDivisible    # if X%2==0, then this will fire (when jumping Y=X)
                INCY
                DJZX isNotDivisible # if X%2==1, this fires (when jumping Y=X)
                INCY
                JMP divisibility    # jump to the beginning of loop

isDivisible:    DJZY main           # this jumps to the main loop with X=X/2
                DJZY main           # this jump will never fire, because X%2==0
                INCX                # for every partition 2 of Y, increase X (making X=Y/2)
                JMP isDivisible     # jump to beginning of loop

isNotDivisible: INCX                # X=0, increase for 1
notDivLoop:     DJZY main           # in each iteration, increase X for 3 (when Y==0, X=3Y+1)
                INCX
                INCX
                INCX
                JMP notDivLoop      # jump to beginning of loop

finish:         HALT                # finally halt

Suministrado con 3 (utilizando el intérprete proporcionado por @orlp), el resultado producido es:

3
10 
5 
16 
8
4
2
1
Nejc
fuente
4

19 instrucciones

Escribí mi propio intérprete porque soy elegante así. Aquí está mi solución para mi propio intérprete:

MP
 XE
 XE
HY
 XV
 XO
 JH
WX
VYM
 JW
LYM
 X
 X
OX
 X
 X
 X
 JL
EH

Y así es como se ve con una sintaxis compatible con el otro intérprete:

# x = n, y = 0
main:    printx
         djzx   end
         djzx   end

# x = n - 2, y = 0 on fallthrough
half:    incy
         djzx   even
         djzx   odd
         jmp    half

evloop:  incx
# x = 0, y = n / 2  on jump to even
even:    djzy   main
         jmp    evloop

oddloop: djzy   main
         incx
         incx
# x = 0, y = (n + 1) / 2 on jump to even
odd:     incx
         incx
         incx
         incx
         jmp    oddloop

end:     halt
FUZxxl
fuente
Parece que encontramos la misma solución, aunque estabas antes :(
orlp
@orlp Eso sucede.
FUZxxl
3

19 instrucciones

found:    PRINTX       # print input/found number
          DJZX done    # check if n == 1
          DJZX done    # after this point x == n - 2
parity:   INCY         # after this loop y == n // 2
          DJZX even
          DJZX odd
          JMP parity
odd-loop: DJZY found
          INCX
          INCX
odd:      INCX         # we enter mid-way to compute x = 6y + 4 = 3n + 1
          INCX
          INCX
          INCX
          JMP odd-loop
even:     DJZY found   # simply set x = y
          INCX
          JMP even
done:     HALT

Puedes ejecutarlo con mi intérprete .

orlp
fuente
Duplicado de mi respuesta .
FUZxxl
@FUZxxl Eso es lo que te dije hace una hora: P
orlp
Si lo hiciste. Escribí esto para que otros se den cuenta de la igualdad.
FUZxxl