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`*
Velocidade vs tamanho
A versão com tamanho otimizado escolhe aleatoriamente as rotações no sentido horário até que a permutação desejada seja alcançada. Isso é suficiente, uma vez que uma rotação no sentido anti-horário é equivalente a três rotações consecutivas no sentido horário do mesmo quadrado.
A versão com velocidade otimizada faz o mesmo, exceto pelo seguinte:
Se o número 1 estiver no canto superior esquerdo, ele não girará mais o quadrado superior esquerdo.
Se o número 9 estiver no canto inferior direito, ele não girará mais o quadrado inferior direito.
As etapas para trocar as posições 7 e 8 são codificadas permanentemente, portanto, existem duas posições que permitem a quebra do loop.
Além de alterar o algoritmo, a versão com velocidade otimizada consegue a rotação de maneira direta, enquanto a versão com tamanho otimizado usa a classificação incorporada do GolfScript por mapeamento. Ele também codifica o estado final (para comparação) em vez de classificar o estado em todas as iterações.
A versão otimizada para velocidade requer menos iterações e cada iteração é muito mais rápida por si só.
Benchmarks
Eu usei o seguinte código para randomizar as posições dos números e executar testes, descomentando a linha correspondente à versão a ser testada:
[{[
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: '\+
A saída mostra o número mínimo e máximo de etapas necessárias para ordenar os números, a média e a mediana de todas as execuções, bem como o tempo decorrido em 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
Na minha máquina (Intel Core i7-3770), o tempo médio de execução da versão com tamanho otimizado foi de 3,58 minutos. O tempo médio de execução da versão com velocidade otimizada foi de 0,20 segundos. Assim, a versão com velocidade otimizada é aproximadamente 1075 vezes mais rápida.
A versão com velocidade otimizada produz 114 vezes menos rotações. A execução de cada rotação é 9,4 vezes mais lenta, principalmente devido à atualização do estado.
I / O
A saída consiste em números de 3 bits. O MSB é definido para rotações no sentido anti-horário, o bit do meio é definido para os quadrados inferiores e o LSB é definido para os quadrados direitos. Assim, 0 (4) é o quadrado superior esquerdo, 1 (5) o superior direito, 2 (6) o inferior esquerdo e 3 (7) o inferior direito.
A versão com velocidade otimizada imprime todas as rotações em uma única linha. A versão com tamanho otimizado imprime uma rotação por linha, seguida pela posição final dos números.
Para a versão com velocidade otimizada, a entrada deve gerar uma matriz contendo os números de 1 a 9 quando avaliados. Para a versão com tamanho otimizado, a entrada deve ser uma sequência sem nova linha final; não é avaliado.
Exemplo é executado:
$ 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 com tamanho otimizado
{ #
. # 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.
A atualização do estado é alcançada da seguinte maneira:
A rotação 2 produz o número inteiro 3 após adicionar 1. Se o estado for "123456789", o fatiamento do estado produzirá "456789".
Logo antes de executar "$", os elementos mais altos da pilha são:
[ 1 2 3 4 5 6 7 8 9 ] { [ 4 5 6 7 8 9 ] ? "1420344440" = }
"$" Executa o bloco uma vez para cada elemento da matriz a ser classificado, depois de pressionar o próprio elemento.
O índice de 1 em “[4 5 6 7 8 9]” é -1 (não está presente); portanto, o último elemento de "1420344440" é pressionado. Isso gera 48, o código ASCII correspondente ao caractere 0. Para 2 e 3, 48 também é pressionado.
Os números inteiros pressionados para 4, 5, 6, 7, 8 e 9 são 49, 52, 50, 48, 51 e 52.
Após a classificação, o primeiro elemento do estado será um dos elementos com 48; o último será um daqueles com 52 anos. A classificação interna é instável em geral, mas verifiquei empiricamente que é estável nesse caso específico.
O resultado é "[1 2 3 7 4 6 8 5 9]", que corresponde a uma rotação no sentido horário do quadrado inferior esquerdo.
Código otimizado para velocidade
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 as rotações 3, 0, 7, 6 e 4 trocam os elementos nas posições 7 e 8, sem alterar as posições dos sete elementos restantes.
...and return as output a sequence of moves representing the moves you must take to return the board back to its original
Isso significa "voltar para1 2 3\n4 5 6\n7 8 9
"? Não sei bem como ler isso.