Giải câu đố Xoay


14

Trên một số điện thoại Nokia cũ, có một biến thể của mười lăm câu đố được gọi là Xoay. Trong biến thể này, thay vì trượt một ô một lần, bạn xoay bốn ô một lần theo một hướng.

Trong trò chơi này, bạn sẽ bắt đầu với một bảng như thế này:

4 9 2
3 5 7
8 1 6

Và bằng cách xoay khối dưới bên trái hai lần theo chiều kim đồng hồ và khối trên bên trái một lần theo chiều kim đồng hồ, bạn sẽ nhận được điều này:

4 9 2
8 3 7
1 5 6

4 9 2
1 8 7
3 5 6

1 4 2
8 9 7
3 5 6

1gạch sẽ ở góc trên bên trái nơi nó được cho là. Cuối cùng, sau một vài lần di chuyển, bạn sẽ kết thúc với:

1 2 3
4 5 6
7 8 9

đó là cấu hình "gốc".

Nhiệm vụ của bạn là xây dựng một chương trình sẽ đưa vào lưới 3x3 các số từ 1 đến 9 (ở bất kỳ định dạng nào bạn chọn) và trả về dưới dạng một chuỗi các bước di chuyển thể hiện các bước bạn phải thực hiện để đưa bảng trở lại ban đầu cấu hình (một lần nữa, trong bất kỳ định dạng bạn chọn). Các bước di chuyển hợp pháp được định nghĩa là di chuyển khối [trên / dưới] - [trái / phải] gồm 4 ô [theo chiều kim đồng hồ / ngược chiều kim đồng hồ].

Chương trình của bạn phải có khả năng giải quyết tất cả các lưới 3x3 có thể (tất cả các hoán vị đều có thể giải được).

Mã ngắn nhất để làm điều này chiến thắng.


...and return as output a sequence of moves representing the moves you must take to return the board back to its originalĐiều này có nghĩa là "trở lại 1 2 3\n4 5 6\n7 8 9"? Tôi không chắc làm thế nào để đọc nó.
undergroundmonorail

Vâng, ý tôi là quay lại 1 2 3 4 5 6 7 8 9.
Joe Z.

1
Tôi nghĩ rằng bảng thứ hai và thứ ba trong ví dụ của bạn nên đổi 3 và 5.
Martin Ender

@JoeZ. Tôi đề nghị sửa đổi nó để tuyên bố rằng giải pháp phải có hiệu suất trường hợp xấu nhất bị ràng buộc.
HostileFork nói không tin tưởng SE

Câu trả lời:


7

GolfScript, 39/83 byte

# 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`*

Tốc độ so với kích thước

Phiên bản tối ưu hóa kích thước sẽ chọn ngẫu nhiên các vòng quay theo chiều kim đồng hồ cho đến khi đạt được hoán vị mong muốn. Điều này là đủ, vì một vòng quay ngược chiều kim đồng hồ tương đương với ba vòng quay theo chiều kim đồng hồ liên tiếp của cùng một hình vuông.

Phiên bản tối ưu hóa tốc độ cũng làm như vậy, ngoại trừ các điều sau:

  1. Nếu số 1 ở góc trên bên trái, nó sẽ không xoay hình vuông phía trên bên trái nữa.

  2. Nếu số 9 ở góc dưới bên phải, nó sẽ không xoay hình vuông bên phải phía dưới nữa.

  3. Các bước để hoán đổi vị trí 7 và 8 được mã hóa cứng, do đó, có hai vị trí cho phép vòng lặp bị phá vỡ.

Ngoài việc thay đổi thuật toán, phiên bản tối ưu hóa tốc độ đạt được sự xoay vòng một cách đơn giản, trong khi phiên bản chọn kích thước sử dụng sắp xếp tích hợp sẵn của GolfScript bằng cách ánh xạ. Nó cũng mã hóa trạng thái cuối cùng (để so sánh) thay vì sắp xếp trạng thái trong mỗi lần lặp.

Phiên bản tối ưu hóa tốc độ yêu cầu số lần lặp ít hơn và mỗi lần lặp lại nhanh hơn nhiều.

Điểm chuẩn

Tôi đã sử dụng đoạn mã sau để chọn ngẫu nhiên các vị trí của các số và thực hiện các lần chạy thử, bỏ ghi chú dòng tương ứng với phiên bản cần kiểm tra:

[{[
    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: '\+

Đầu ra cho thấy số bước tối thiểu và tối đa cần thiết để sắp xếp các số, trung bình và trung bình của tất cả các lần chạy, cũng như thời gian trôi qua tính bằng giây:

$ 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

Trên máy của tôi (Intel Core i7-3770), thời gian thực hiện trung bình của phiên bản được tối ưu hóa kích thước là 3,58 phút. Thời gian thực hiện trung bình của phiên bản tối ưu hóa tốc độ là 0,20 giây. Do đó, phiên bản tối ưu hóa tốc độ nhanh hơn khoảng 1075 lần.

Phiên bản tối ưu hóa tốc độ mang lại số lần quay ít hơn 114 lần. Thực hiện mỗi vòng quay chậm hơn 9,4 lần, điều này chủ yếu là do cách thức cập nhật trạng thái.

Tôi / O

Đầu ra bao gồm các số 3 bit. MSB được đặt cho các vòng quay ngược chiều kim đồng hồ, bit giữa được đặt cho các ô vuông thấp hơn và LSB được đặt cho các ô vuông bên phải. Do đó, 0 (4) là hình vuông trên bên trái, 1 (5) bên trên bên phải, 2 (6) bên dưới bên trái và 3 (7) bên dưới bên phải.

Phiên bản tối ưu hóa tốc độ in tất cả các phép quay trên một dòng. Phiên bản tối ưu hóa kích thước in một vòng quay trên mỗi dòng, theo sau là vị trí cuối cùng của các số.

Đối với phiên bản được tối ưu hóa tốc độ, đầu vào phải tạo ra một mảng chứa các số từ 1 đến 9 khi được đánh giá. Đối với phiên bản được tối ưu hóa kích thước, đầu vào phải là một chuỗi không có dòng mới cuối cùng; nó không được đánh giá.

Ví dụ chạy:

$ echo -n '253169748' | golfscript rotation-size.gs
3
0
123456789
$ golfscript rotation-speed.gs <<< '[5 4 7 1 2 9 3 8 6]'
2210300121312212222212211121122211122221211111122211211222112230764

Mã tối ưu hóa kích thước

{               #
  .             # 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.

Cập nhật trạng thái đạt được theo cách sau:

Xoay 2 mang lại số nguyên 3 sau khi thêm 1. Nếu trạng thái là 123456789, cắt, trạng thái mang lại lợi ích 456789.

Ngay trước khi thực hiện, $ $, các yếu tố trên cùng của ngăn xếp là:

[ 1 2 3 4 5 6 7 8 9 ] { [ 4 5 6 7 8 9 ] ? "1420344440" = }

Một trong những phần tử của mảng được thực hiện một lần cho mỗi phần tử của mảng được sắp xếp, sau khi tự đẩy phần tử đó.

Chỉ số 1 trong Tiếng [4 5 6 7 8 9] Giáp là -1 (không có mặt), do đó, yếu tố cuối cùng của "1420344440" được đẩy. Điều này mang lại 48, mã ASCII tương ứng với ký tự 0. Trong 2 và 3, 48 cũng được đẩy.

Các số nguyên được đẩy cho 4, 5, 6, 7, 8 và 9 là 49, 52, 50, 48, 51 và 52.

Sau khi sắp xếp, phần tử đầu tiên của trạng thái sẽ là một trong những phần tử mang lại 48; cái cuối cùng sẽ là một trong những cái mang lại 52. Nói chung, loại tích hợp không ổn định, nhưng tôi đã xác minh theo kinh nghiệm rằng nó ổn định trong trường hợp cụ thể này.

Kết quả là ra [1 2 3 7 4 6 8 5 9], tương ứng với một vòng quay theo chiều kim đồng hồ của hình vuông bên trái phía dưới.

Mã tối ưu hóa tốc độ

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".

Quan sát rằng các phép quay 3, 0, 7, 6 và 4 hoán đổi các phần tử ở vị trí 7 và 8, mà không làm thay đổi vị trí của bảy phần tử còn lại.


Tối ưu hóa cho tốc độ? Đó là Golfscript ...
"

1
@Synthetica: Tuy nhiên, đó là giải pháp nhanh nhất đã được đăng cho đến nay.
Dennis

4

Python với Numpy - 158

from numpy import*
A=input()
while any(A.flat>range(1,10)):i,j,k=random.randint(0,2,3);A[i:i+2,j:j+2]=rot90(A[i:i+2,j:j+2],1+2*k);print"tb"[i]+"lr"[j]+"wc"[k]

Đầu vào phải có định dạng sau:

array([[1,2,5],[4,3,6],[7,8,9]])

Mỗi dòng đầu ra là một động thái được mã hóa theo chuỗi như trwhoặc blcvà được đọc như sau:

  • t: hàng đầu
  • b: dưới cùng
  • l: trái
  • r: đúng
  • c: chiều kim đồng hồ
  • w: ngược chiều kim đồng hồ (widderhins)

Chương trình này thực hiện di chuyển ngẫu nhiên cho đến khi đạt được cấu hình đích. Theo giả định gần đúng rằng mọi di chuyển đều có xác suất độc lập là 1/9! để đạt cấu hình đích target, số lần quay trước khi giải pháp được phân phối theo cấp số nhân với giá trị trung bình (nghĩa là số lần di chuyển trung bình) là 9! ≈ 3,6 · 10⁵. Điều này phù hợp với một thử nghiệm ngắn (20 lần chạy).

¹ 9! là tổng số cấu hình.


2
Vì vậy, về cơ bản, nó cố gắng di chuyển ngẫu nhiên cho đến khi đạt được một giải pháp?
Joe Z.

Làm việc cho tôi. Mặc dù tôi quan tâm đến số lần quay dự kiến ​​trước khi có thể đạt được giải pháp.
Joe Z.

@JoeZ.: Xem chỉnh sửa bài viết của tôi.
Wrzlprmft

Thật tuyệt vời.
Kyle Kanos

4

Giải pháp di chuyển ít nhất của C ++ - chiều rộng đầu tiên (1847 ký tự.)

Sau một chút suy nghĩ, tôi nghĩ rằng tôi đã làm điều này hiệu quả hơn và hợp lý hơn. Giải pháp này, trong khi nó chắc chắn không giành được giải golf này, cho đến nay là người duy nhất sẽ cố gắng tìm ra số vòng quay ngắn nhất sẽ giải quyết ván. Cho đến nay, nó giải quyết mọi bảng ngẫu nhiên mà tôi đã ném vào nó trong chín hoặc ít hơn di chuyển. Nó cũng hoạt động tốt hơn đáng kể so với lần trước của tôi và, hy vọng, giải quyết các bình luận của Dennis bên dưới.

Từ giải pháp trước đó, thay đổi lớn nhất là chuyển lịch sử quan trọng từ trạng thái hội đồng quản trị (BS) sang một lớp mới lưu trữ lịch sử ở độ sâu nhất định (DKH). Bất cứ khi nào ứng dụng thực hiện di chuyển, nó sẽ kiểm tra lịch sử ở độ sâu đó và tất cả các độ sâu trước để xem nó có được đánh giá hay không, nếu vậy, nó sẽ không được thêm vào hàng đợi nữa. Điều này dường như làm giảm đáng kể dung lượng lưu trữ trên hàng đợi (bằng cách xóa tất cả lịch sử này khỏi trạng thái bảng) và do đó giảm khá nhiều tất cả các cách cắt tỉa ngu ngốc mà tôi phải làm để giữ cho mã không bị hết bộ nhớ. Thêm vào đó, nó chạy nhanh hơn rất nhiều vì có rất ít để sao chép vào hàng đợi.

Bây giờ, đó là một tìm kiếm đầu tiên đơn giản trên các tiểu bang khác nhau. Thêm vào đó, tôi muốn thay đổi bộ khóa (hiện được lưu dưới dạng tập hợp số trong cơ sở-9, mỗi số được tính bởi khóa BS :: là đại diện cơ sở-9 của bảng) thành một bitet có 9! bit dường như là không cần thiết; mặc dù tôi đã tìm ra cách tính một khóa trong "hệ thống số giai thừa" có thể được sử dụng để tính toán bit trong bitet để kiểm tra / chuyển đổi.

Vì vậy, giải pháp mới nhất là:

#include <iostream>
#include <list>
#include <set>
#include <vector>
using namespace std;
struct BS{
#define LPB(i) for(int*i=b;i-b<9;i++)
struct ROP{int t, d;};
typedef vector<ROP> SV;
typedef unsigned int KEY;
typedef set<KEY> KH;
BS(const int*d){const int*x=d;int*y=b;for(;x-d<9;x++,y++)*y=*x;}
BS(){LPB(i)*i=i-b+1;}
bool solved(){LPB(i)if(i-b+1!=*i)return 0;return 1;}
void rot(int t, int d){return rot((ROP){t,d});}
void rot(ROP r){rotb(r);s.push_back(r);}
bool undo(){if (s.empty())return false;ROP &u=s.back();u.d*=-1;rotb(u);s.pop_back();return true;}
SV &sol(){return s;}
KEY key(){KEY rv=0;LPB(i){rv*=9;rv+=*i-1;}return rv;}
int b[9];
SV s;
void rotb(ROP r){int c=r.t<2?r.t:r.t+1;int bi=(r.d>0?3:4)+c;const int*ri=r.d>0?(const int[]){0,1,4}:(const int[]){1,0,3};for(int i=0;i<3;i++)swap(b[bi],b[c+ri[i]]);}
};
ostream &operator<<(ostream &o, BS::ROP r){static const char *s[]={"tl","tr","bl","br"};o<<s[r.t]<<(r.d<0?"w":"c");return o;}
struct DKH{
~DKH(){for(HV::iterator i=h.begin();i<h.end();++i)if(*i!=NULL)delete *i;}
void add(int d,BS b){h.resize(d+1);if(h[d]==NULL)h[d]=new BS::KH();h[d]->insert(b.key());}
bool exists(BS &b){BS::KEY k=b.key();size_t d=min(b.sol().size(),h.size()-1);do if (h[d]->find(k)!=h[d]->end())return true;while(d--!=0);return false;}
typedef vector<BS::KH *> HV;HV h;
};
static bool solve(BS &b)
{
const BS::ROP v[8]={{0,-1},{0,1},{1,-1},{1,1},{2,-1},{2,1},{3,-1},{3,1}};
DKH h;h.add(0,b);
list<BS> q;q.push_back(b);
while (!q.empty())
{
BS qb=q.front();q.pop_front();
if (qb.solved()){b=qb;return true;}
int d=qb.sol().size()+1;
for (int m=0;m<8;++m){qb.rot(v[m]);if (!h.exists(qb)){h.add(d,qb);q.push_back(qb);}qb.undo();}
}
return false;
}
int main()
{
BS b((const int[]){4,9,2,3,5,7,8,1,6});
if (solve(b)){BS::SV s=b.sol();for(BS::SV::iterator i=s.begin();i!=s.end();++i)cout<<*i<<" ";cout<<endl;}
}

1
Mã của bạn trông giống như C ++ thay vì C.
user12205

@ace, thực sự là nó, đã sửa.
DreamWar Warrior

Trong trường hợp bất kỳ ai khác có vấn đề biên dịch điều này với g ++, tôi đã phải thay đổi tất cả các trường hợp int[]thành const int[]và đặt cờ -fpermissive.
Dennis

@Dennis, Xin lỗi về điều đó, tôi đã biên dịch nó với hai trình biên dịch g ++ riêng biệt và dường như không bận tâm. Nhưng, tôi có thể thấy một phiên bản mới hơn, chặt chẽ hơn sẽ rên rỉ như thế nào. Cảm ơn.
DreamWar Warrior

Biên dịch tốt ngay bây giờ và nó nhanh hơn rất nhiều. Giải quyết các bình luận bạn đã xóa khỏi câu hỏi: Có một số hoán vị dường như cần 11 bước. 980654321 là một trong số đó.
Dennis

1

CJam - 39

l{4mr_o_1>+_@m<_[Z0Y4X]\f=\5>+m>__$>}g;

Một bộ giải ngẫu nhiên khác :)
Nó lấy một chuỗi như 492357816 và xuất ra một chuỗi các chữ số (dài) từ 0 đến 3, mỗi chuỗi biểu thị một vòng quay theo chiều kim đồng hồ của một khối: 0 = trên cùng bên trái, 1 = trên cùng bên phải, 2 = dưới cùng -left, 3 = dưới cùng bên phải.

Giải thích ngắn gọn:

4mrtạo một số ngẫu nhiên từ 0 đến 3
_1>+tăng số nếu nó lớn hơn 1 (vì vậy chúng tôi kết thúc bằng 0, 1, 3 hoặc 4 - các chỉ số bắt đầu của 4 khối)
m<xoay chuỗi sang trái (chẳng hạn như 492357816 -> 923578164, không phải xoay khối) để đưa khối xoay ở vị trí đầu tiên
[Z0Y4X]\f=, xoay khối sẽ ảnh hưởng đến 5 ký tự đầu tiên, chẳng hạn như 12345 -> 41352;
X = 1, Y = 2, Z = 3 vì vậy [Z0Y4X] thực sự là [3 0 2 4 1] và đó là các chỉ mục dựa trên 0 của các ô được xoay
5>sao chép phần còn lại của chuỗi
m>xoay chuỗi (đã sửa đổi) trở lại
__$>kiểm tra đúng nếu chuỗi được sắp xếp (đó là điều kiện dừng)


1

Toán học, 104 ký tự

Chúng ta có thể diễn giải nhiệm vụ bằng ngôn ngữ của các nhóm hoán vị. Bốn phép quay chỉ là bốn hoán vị tạo ra nhóm đối xứng S 9 , và nhiệm vụ chỉ là viết một hoán vị như một sản phẩm của các máy phát. Mathematica có một chức năng tích hợp để làm điều này.

i={1,2,5,4};GroupElementToWord[PermutationGroup[Cycles/@({i}+#&/@i-1)],Input[]~FindPermutation~Range@9]

Thí dụ:

Đầu vào:

{4, 9, 2, 8, 3, 7, 1, 5, 6}

Đầu ra:

{-2, -3, -4, 2, 4, 1, 4, -1, -2, 3, 2, -4, 3, 4, -3, -3, -4, -4, -2, -2, -3, -2, 3, -1}
  • 1: trên cùng bên trái theo chiều kim đồng hồ
  • 2: trên cùng bên phải theo chiều kim đồng hồ
  • 3: dưới cùng bên phải theo chiều kim đồng hồ
  • 4: dưới cùng bên trái theo chiều kim đồng hồ
  • -1: trên cùng bên trái ngược chiều kim đồng hồ
  • -2: trên cùng bên phải ngược chiều kim đồng hồ
  • -3: dưới cùng bên phải ngược chiều kim đồng hồ
  • -4: dưới cùng bên trái ngược chiều kim đồng hồ
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.