GolfScript, 39/83 octets
# 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`*
Vitesse vs taille
La version à taille optimisée choisit au hasard des rotations dans le sens horaire jusqu'à ce que la permutation souhaitée soit atteinte. Cela suffit, car une rotation dans le sens antihoraire équivaut à trois rotations consécutives dans le sens horaire du même carré.
La version à vitesse optimisée fait de même, à l'exception des éléments suivants:
Si le chiffre 1 est dans le coin supérieur gauche, il ne fait plus tourner le carré supérieur gauche.
Si le chiffre 9 est dans le coin inférieur droit, il ne fait plus tourner le carré inférieur droit.
Les étapes de permutation des positions 7 et 8 sont codées en dur, il y a donc deux positions qui permettent à la boucle de se rompre.
Outre la modification de l'algorithme, la version à vitesse optimisée réalise la rotation d'une manière simple, tandis que la version à taille optimisée utilise le tri intégré de GolfScript par mappage. Il code également en dur l'état final (à titre de comparaison) au lieu de trier l'état à chaque itération.
La version à vitesse optimisée nécessite moins d'itérations et chaque itération est beaucoup plus rapide en elle-même.
Repères
J'ai utilisé le code suivant pour randomiser les positions des nombres et effectuer des tests, sans commenter la ligne correspondant à la version à tester:
[{[
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 sortie indique le nombre minimum et maximum d'étapes nécessaires pour ordonner les nombres, la moyenne et la médiane de tous les cycles, ainsi que le temps écoulé en secondes:
$ 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
Sur ma machine (Intel Core i7-3770), le temps d'exécution moyen de la version à taille optimisée était de 3,58 minutes. Le temps d'exécution moyen de la version à vitesse optimisée était de 0,20 seconde. Ainsi, la version à vitesse optimisée est environ 1075 fois plus rapide.
La version à vitesse optimisée produit 114 fois moins de rotations. L'exécution de chaque rotation est 9,4 fois plus lente, principalement en raison de la mise à jour de l'état.
E / S
La sortie se compose de nombres à 3 bits. Le MSB est réglé pour les rotations dans le sens antihoraire, le bit du milieu est réglé pour les carrés inférieurs et le LSB est réglé pour les carrés droits. Ainsi, 0 (4) est le carré supérieur gauche, 1 (5) le coin supérieur droit, 2 (6) le coin inférieur gauche et 3 (7) le coin inférieur droit.
La version à vitesse optimisée imprime toutes les rotations sur une seule ligne. La version à taille optimisée imprime une rotation par ligne, suivie de la position finale des chiffres.
Pour la version à vitesse optimisée, l'entrée doit produire un tableau contenant les nombres de 1 à 9 lors de l'évaluation. Pour la version à taille optimisée, l'entrée doit être une chaîne sans retour à la ligne final; il n'est pas évalué.
L'exemple s'exécute:
$ echo -n '253169748' | golfscript rotation-size.gs
3
0
123456789
$ golfscript rotation-speed.gs <<< '[5 4 7 1 2 9 3 8 6]'
2210300121312212222212211121122211122221211111122211211222112230764
Code optimisé pour la taille
{ #
. # 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 mise à jour de l'état est réalisée de la manière suivante:
La rotation 2 donne l'entier 3 après avoir ajouté 1. Si l'état est «123456789», le découpage de l'état donne «456789».
Juste avant d'exécuter «$», les éléments supérieurs de la pile sont:
[ 1 2 3 4 5 6 7 8 9 ] { [ 4 5 6 7 8 9 ] ? "1420344440" = }
«$» Exécute le bloc une fois pour chaque élément du tableau à trier, après avoir poussé l'élément lui-même.
L'index de 1 dans «[4 5 6 7 8 9]» est -1 (non présent), donc le dernier élément de «1420344440» est poussé. Cela donne 48, le code ASCII correspondant au caractère 0. Pour 2 et 3, 48 est également poussé.
Les entiers poussés pour 4, 5, 6, 7, 8 et 9 sont 49, 52, 50, 48, 51 et 52.
Après le tri, le premier élément de l'état sera l'un des éléments donnant 48; le dernier sera l'un de ceux qui donneront 52. Le tri intégré est instable en général, mais j'ai vérifié empiriquement qu'il est stable dans ce cas particulier.
Le résultat est «[1 2 3 7 4 6 8 5 9]», ce qui correspond à une rotation dans le sens horaire du carré inférieur gauche.
Code optimisé pour la vitesse
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".
Observez que les rotations 3, 0, 7, 6 et 4 permutent les éléments dans les positions 7 et 8, sans modifier les positions des sept éléments restants.
...and return as output a sequence of moves representing the moves you must take to return the board back to its original
Est-ce à dire "retour à1 2 3\n4 5 6\n7 8 9
"? Je ne sais pas comment lire ça.