GolfScript, 39/83 bytes
# Optimized for size:
{.4rand.p.2/+>`{?1420344440`=}+$..$>}do
# Optimized for speed:
6,(7++:t;~{.(1=.@7=9=+4\-rand+..2/+@.@>:s^[3s=0s=2s=4s=1s=]+s|.)9<\t>|}do.$>30764`*
Velocidad vs tamaño
La versión de tamaño optimizado elige aleatoriamente rotaciones en el sentido de las agujas del reloj hasta lograr la permutación deseada. Esto es suficiente, ya que una rotación en sentido antihorario es equivalente a tres rotaciones consecutivas en sentido horario del mismo cuadrado.
La versión con velocidad optimizada hace lo mismo, excepto por lo siguiente:
Si el número 1 está en la esquina superior izquierda, ya no gira el cuadrado superior izquierdo.
Si el número 9 está en la esquina inferior derecha, ya no gira el cuadrado inferior derecho.
Los pasos para intercambiar las posiciones 7 y 8 están codificados, por lo que hay dos posiciones que permiten que el bucle se rompa.
Además de cambiar el algoritmo, la versión con velocidad optimizada logra la rotación de una manera directa, mientras que la versión con tamaño optimizado utiliza el ordenamiento incorporado de GolfScript por mapeo. También codifica el estado final (para comparación) en lugar de ordenar el estado en cada iteración.
La versión con velocidad optimizada requiere menos iteraciones y cada iteración es mucho más rápida por sí misma.
Puntos de referencia
He utilizado el siguiente código para aleatorizar las posiciones de los números y realizar ejecuciones de prueba, descomentando la línea correspondiente a la versión a probar:
[{[
0:c;10,1>{;2 32?rand}$
#{c):c;.4rand.2/+>`{?1420344440`=}+$..$>}do
#6,(7++:t;{.(1=.@7=9=+4\-rand+..2/+@.@>:s^[3s=0s=2s=4s=1s=]+s|.)9<\t>|}do.$>30764`*
],c+}\~*]
$.0='Min: '\+puts .-1='Max: '\+puts ..{+}*\,/'Avg: '\+puts .,2/='Med: '\+
La salida muestra el número mínimo y máximo de pasos necesarios para ordenar los números, el promedio y la mediana de todas las ejecuciones, así como el tiempo transcurrido en segundos:
$ TIME='\n%e s' time golfscript rotation-test-size.gs <<< 100
Min: 4652
Max: 2187030
Avg: 346668
Med: 216888
21500.10 s
$
$ TIME='\n%e s' time golfscript rotation-test-speed.gs <<< 1000
Min: 26
Max: 23963
Avg: 3036
Med: 2150
202.62 s
En mi máquina (Intel Core i7-3770), el tiempo medio de ejecución de la versión de tamaño optimizado fue de 3.58 minutos. El tiempo medio de ejecución de la versión con velocidad optimizada fue de 0,20 segundos. Por lo tanto, la versión con velocidad optimizada es aproximadamente 1075 veces más rápida.
La versión con velocidad optimizada produce 114 veces menos rotaciones. Realizar cada rotación es 9.4 veces más lento, lo que se debe principalmente a cómo se actualiza el estado.
I / O
La salida consta de números de 3 bits. El MSB está configurado para rotaciones en sentido antihorario, el bit central está configurado para cuadrados más bajos y el LSB está configurado para cuadrados derechos. Por lo tanto, 0 (4) es el cuadrado superior izquierdo, 1 (5) el superior derecho, 2 (6) el inferior izquierdo y 3 (7) el inferior derecho.
La versión con velocidad optimizada imprime todas las rotaciones en una sola línea. La versión de tamaño optimizado imprime una rotación por línea, seguida de la posición final de los números.
Para la versión con velocidad optimizada, la entrada debe producir una matriz que contenga los números del 1 al 9 cuando se evalúa. Para la versión de tamaño optimizado, la entrada debe ser una cadena sin nueva línea final; No se evalúa.
Ejecuciones de ejemplo:
$ echo -n '253169748' | golfscript rotation-size.gs
3
0
123456789
$ golfscript rotation-speed.gs <<< '[5 4 7 1 2 9 3 8 6]'
2210300121312212222212211121122211122221211111122211211222112230764
Código de tamaño optimizado
{ #
. # Duplicate the state.
4rand # Push a randomly chosen integers between 0 and 3.
.p # Print that integer.
.2/+ # Add 1 to it if it is grater than one. Possible results: 0, 1, 3, 4
>` # Slice the state at the above index.
{ # Push a code block doing the following:
? # Get the index of the element of the iteration in the sliced state.
1420344440` # Push the string "14020344440".
= # Retrieve the element at the position of the computed index.
}+ # Concatenate the code block with the sliced state.
$ # Sort the state according to the above code block. See below.
..$> # Push two copies of the state, sort the second and compare the arrays.
}do # If the state is not sorted, repeat the loop.
La actualización del estado se logra de la siguiente manera:
La rotación 2 produce el número entero 3 después de sumar 1. Si el estado es "123456789", al dividir el estado se obtiene "456789".
Justo antes de ejecutar "$", los elementos superiores de la pila son:
[ 1 2 3 4 5 6 7 8 9 ] { [ 4 5 6 7 8 9 ] ? "1420344440" = }
"$" Ejecuta el bloque una vez para cada elemento de la matriz que se ordenará, después de presionar el elemento en sí.
El índice de 1 en “[4 5 6 7 8 9]” es -1 (no presente), por lo que se empuja el último elemento de "1420344440". Esto produce 48, el código ASCII correspondiente al carácter 0. Para 2 y 3, 48 también se empuja.
Los enteros presionados para 4, 5, 6, 7, 8 y 9 son 49, 52, 50, 48, 51 y 52.
Después de ordenar, el primer elemento del estado será uno de los elementos que produzcan 48; el último será uno de los que produzcan 52. El tipo incorporado es inestable en general, pero he comprobado empíricamente que es estable en este caso particular.
El resultado es "[1 2 3 7 4 6 8 5 9]", que corresponde a una rotación en el sentido de las agujas del reloj del cuadrado inferior izquierdo.
Código de velocidad optimizada
6,(7++:t; # Save [ 1 2 3 4 5 7 ] in variable “t” and discard it.
~ # Interpret the input string.
{ #
:s # Duplicate the current state.
(1= # Unshift the first element and push 1 if it is equal to 1 and 0 otherwise.
.@ # Duplicate the boolean and rotate the unshifted array on top of it.
7=9= # Push 1 if the eighth element of “s” is equal to 9 and 0 otherwise.
+4\- # Add the booleans and subtract their sum from 4.
rand # Push a randomly chosen integers between 0 and the result from above.
+. # Add this integer to the first boolean and duplicate it for the output.
.2/+ # Add 1 to the result if it is grater than one. Possible results: 0, 1, 3, 4
@. # Rotate the state on top of the stack and duplicate it.
@>:s # Slice the state at the integer from above and save the result in “s”.
^ # Compute the symmetric difference of state and sliced state.
[ # Apply a clockwise rotation to the sliced array:
3s= # The fourth element becomes the first.
0s= # The first element becomes the second.
2s= # The third element remains the same.
4s= # The fifth element becomes the fourth.
1s= # The second element becomes the fifth.
] # Collect the results into an array.
+ # Concatenate with array of elements preceding the slice.
s| # Perform set union to add the remaining elements of “s”.
. # Duplicate the updated state.
)9< # Pop the last element; push 0 if it is equal to 9 and 1 otherwise.
\t # Swap the popped state on top and push [ 1 2 3 4 5 7 ].
> # Push 0 if the state begins with [ 1 2 3 4 5 6 ] and 1 otherwise.
| # Take the logical OR of the booleans.
}do # If the resulting boolean is 1, repeat the loop.
.$ # Duplicate the state and sort it.
>30764`* # If the state was not sorted, 7 and 8 are swapped, so push "30764".
Observe que las rotaciones 3, 0, 7, 6 y 4 intercambian los elementos en las posiciones 7 y 8, sin alterar las posiciones de los siete elementos restantes.
...and return as output a sequence of moves representing the moves you must take to return the board back to its original
¿Esto significa "volver a1 2 3\n4 5 6\n7 8 9
"? No estoy seguro de cómo leer eso.