GolfScript, 39/83 байта
# 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`*
Скорость против размера
Оптимизированная по размеру версия случайным образом выбирает вращения по часовой стрелке, пока не будет достигнута нужная перестановка. Этого достаточно, поскольку вращение против часовой стрелки эквивалентно трем последовательным вращениям по часовой стрелке одного и того же квадрата.
Оптимизированная по скорости версия делает то же самое, за исключением следующего:
Если число 1 находится в верхнем левом углу, оно больше не поворачивает верхний левый квадрат.
Если число 9 находится в нижнем правом углу, оно больше не вращает нижний правый квадрат.
Шаги для изменения позиций 7 и 8 жестко закодированы, поэтому есть две позиции, которые позволяют разрывать петлю.
Помимо изменения алгоритма, оптимизированная по скорости версия выполняет вращение простым способом, тогда как оптимизированная по размеру версия использует встроенную сортировку GolfScript путем сопоставления. Он также жестко кодирует конечное состояние (для сравнения) вместо сортировки состояния в каждой итерации.
Оптимизированная по скорости версия требует меньше итераций, и каждая итерация намного быстрее сама по себе.
Ориентиры
Я использовал следующий код для рандомизации позиций чисел и выполнения тестовых прогонов, раскомментировав строку, соответствующую тестируемой версии:
[{[
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: '\+
Выходные данные показывают минимальное и максимальное количество шагов, необходимых для упорядочения чисел, среднего значения и медианы всех прогонов, а также истекшее время в секундах:
$ 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
На моей машине (Intel Core i7-3770) среднее время выполнения оптимизированной по размеру версии составило 3,58 минуты. Среднее время выполнения оптимизированной по скорости версии составило 0,20 секунды. Таким образом, оптимизированная по скорости версия примерно в 1075 раз быстрее.
Оптимизированная по скорости версия дает в 114 раз меньше оборотов. Выполнение каждого поворота происходит в 9,4 раза медленнее, что в основном связано с тем, как обновляется состояние.
I / O
Выход состоит из 3-битных чисел. MSB установлен для вращения против часовой стрелки, средний бит установлен для нижних квадратов, а LSB установлен для правых квадратов. Таким образом, 0 (4) - верхний левый квадрат, 1 (5) - верхний правый, 2 (6) - нижний левый и 3 (7) - нижний правый.
Оптимизированная по скорости версия печатает все повороты в одну строку. Оптимизированная по размеру версия печатает один оборот на строку с последующим окончательным положением чисел.
Для версии с оптимизированной скоростью входные данные должны давать массив, содержащий цифры от 1 до 9 при оценке. Для версии с оптимизированным размером входные данные должны быть строкой без заключительного перевода строки; это не оценивается.
Пример работы:
$ echo -n '253169748' | golfscript rotation-size.gs
3
0
123456789
$ golfscript rotation-speed.gs <<< '[5 4 7 1 2 9 3 8 6]'
2210300121312212222212211121122211122221211111122211211222112230764
Оптимизированный по размеру код
{ #
. # 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.
Обновление состояния достигается следующим образом:
Вращение 2 дает целое число 3 после добавления 1. Если состояние «123456789», то разделение состояния дает «456789».
Прямо перед выполнением «$» самые верхние элементы стека:
[ 1 2 3 4 5 6 7 8 9 ] { [ 4 5 6 7 8 9 ] ? "1420344440" = }
«$» Выполняет блок один раз для каждого элемента массива, который будет отсортирован, после нажатия самого элемента.
Индекс 1 в «[4 5 6 7 8 9]» равен -1 (отсутствует), поэтому последний элемент «1420344440» помещается. Это дает 48, код ASCII, соответствующий символу 0. Для 2 и 3, 48 также выдвигается.
Целые числа, заданные для 4, 5, 6, 7, 8 и 9: 49, 52, 50, 48, 51 и 52.
После сортировки первым элементом состояния будет один из элементов, дающих 48; последний будет одним из тех, которые дают 52. Встроенный тип сортировки в целом нестабилен, но я эмпирически подтвердил, что в данном конкретном случае он стабилен.
Результат - «[1 2 3 7 4 6 8 5 9]», что соответствует вращению нижнего левого квадрата по часовой стрелке.
Оптимизированный по скорости код
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".
Обратите внимание, что повороты 3, 0, 7, 6 и 4 меняют местами элементы в позициях 7 и 8, не изменяя положения остальных семи элементов.
...and return as output a sequence of moves representing the moves you must take to return the board back to its original
Означает ли это "вернуться к1 2 3\n4 5 6\n7 8 9
"? Я не уверен, как это читать.