회전 퍼즐 풀기


14

일부 노키아 폰에서는 Rotation이라는 15 개의 퍼즐 이 변형되었습니다 . 이 변형에서는 한 번에 하나의 타일을 슬라이딩하는 대신 한 방향으로 한 번에 4 개의 타일을 회전했습니다.

이 게임에서는 다음과 같은 보드로 시작합니다.

4 9 2
3 5 7
8 1 6

왼쪽 하단 블록을 시계 방향으로 두 번 회전하고 왼쪽 상단 블록을 시계 방향으로 한 번 회전하면 다음과 같은 결과를 얻을 수 있습니다.

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

그리고 1타일이해야하는데 왼쪽 상단 구석에있을 것입니다. 결국 몇 번 더 움직 인 후에는 다음과 같이 끝납니다.

1 2 3
4 5 6
7 8 9

"원본"구성입니다.

당신의 임무는 1에서 9까지의 숫자의 3x3 그리드를 입력으로 선택하고 (원하는 형식으로) 보드를 원래대로 되 돌리는 데 필요한 동작을 나타내는 일련의 동작을 출력으로 반환하는 프로그램을 작성하는 것입니다. 구성 (다시 선택한 형식으로). 합법적 인 움직임은 4 타일의 [위 / 아래]-[왼쪽 / 오른쪽] 블록을 [시계 방향 / 반 시계 방향]으로 움직이는 것으로 정의됩니다.

프로그램은 가능한 모든 3x3 그리드를 풀 수 있어야합니다 (모든 순열은 해결할 수 있습니다).

이 작업을 수행하는 가장 짧은 코드가 이깁니다.


...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"를 의미합니까 ? 어떻게 읽는지 잘 모르겠습니다.
undergroundmonorail

예, 다시 1 2 3 4 5 6 7 8 9로 돌아갑니다
.

1
귀하의 예제에서 두 번째와 세 번째 보드는 3과 5가 바뀌어야한다고 생각합니다.
Martin Ender

@JoeZ. 솔루션에 한계 최악의 성능이 있어야한다고 선언하도록 수정하는 것이 좋습니다.
HostileFork는 SE를

답변:


7

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

속도 대 크기

크기 최적화 버전은 원하는 순열이 달성 될 때까지 시계 방향 회전을 임의로 선택합니다. 반 시계 방향 회전은 동일한 정사각형의 3 회 연속 시계 방향 회전과 동일하므로 충분합니다.

속도 최적화 버전은 다음을 제외하고 동일하게 수행됩니다.

  1. 숫자 1이 왼쪽 위 모서리에 있으면 더 이상 왼쪽 위 사각형이 회전하지 않습니다.

  2. 숫자 9가 오른쪽 하단에 있으면 오른쪽 하단 사각형이 더 이상 회전하지 않습니다.

  3. 위치 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는 1을 더한 후 정수 3을 산출합니다. 상태가 "123456789"이면 상태를 슬라이스하면 "456789"가됩니다.

"$"를 실행하기 직전에 스택의 최상위 요소는 다음과 같습니다.

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

“$”는 요소 자체를 푸시 한 후 배열의 모든 요소가 정렬 될 때마다 블록을 한 번 실행합니다.

"[4 5 6 7 8 9]"에서 1의 인덱스는 -1 (없음)이므로 "1420344440"의 마지막 요소가 푸시됩니다. 문자 0에 해당하는 ASCII 코드 인 48이 생성됩니다. 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 개 요소의 위치를 ​​변경하지 않고 위치 7과 8의 요소를 교체합니다.


속도에 최적화되어 있습니까? 그것은 Golfscript입니다 ...
ɐɔıʇǝɥʇuʎ 's

1
@ Synthetica : 그럼에도 불구하고 지금까지 게시 된 가장 빠른 솔루션입니다.
Dennis

4

Numpy가 포함 된 Python – 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]

입력 형식은 다음과 같아야합니다.

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

각 출력 라인과 같이 문자열로 인코딩 된 움직임이다 trw또는 blc다음과 같이 판독 될 :

  • t: 상단
  • b: 바닥
  • l: 왼쪽
  • r: 권리
  • c: 시계 방향
  • w: 시계 반대 방향 (위 더신)

이 프로그램은 대상 구성에 도달 할 때까지 임의 이동을 수행합니다. 모든 움직임은 1/9의 독립적 확률을 갖는다는 근사적인 가정하에! 목표 구성 ¹에 도달하기 위해, 솔루션이 평균 9의 평균 (즉, 평균 이동 수)으로 지수 적으로 분배되기 전의 회전 수! ≈ 3.6 · 10⁵. 이것은 짧은 실험 (20 회)에 따른 것입니다.

¹ 9! 총 구성 수입니다.


2
따라서 본질적으로 솔루션을 얻을 때까지 무작위 이동을 시도합니까?
Joe Z.

나를 위해 작동합니다. 솔루션에 도달하기 전에 예상되는 회전 수에 관심이 있지만
Joe Z.

@JoeZ .: 내 게시물 수정 사항을 참조하십시오.
Wrzlprmft

대단해.
Kyle Kanos

4

C ++ 최소 이동 솔루션-너비 우선 (1847 자)

조금 더 생각한 후에, 나는 이것이 훨씬 더 효율적이고 현명하게 이루어 졌다고 생각합니다. 이 솔루션은 확실히이 골프에서이기는 것은 아니지만 보드를 해결하는 가장 짧은 회전 수를 찾는 유일한 방법입니다. 지금까지 9 번 이하의 움직임으로 내가 던진 모든 무작위 보드를 해결합니다. 또한 마지막 것보다 성능이 훨씬 뛰어나며 아래의 Dennis 의견을 다룹니다.

이전 솔루션에서 가장 큰 변화는 주요 히스토리를 보드 상태 (BS)에서 히스토리를 지정된 깊이 (DKH)로 저장하는 새 클래스로 이동하는 것이 었습니다. 응용 프로그램이 이동할 때마다 응용 프로그램은 해당 깊이와 모든 깊이에서 기록을 확인하여 평가 된 적이 있는지 확인한 다음 다시 큐에 추가되지 않습니다. 이것은 보드 상태 자체 에서이 모든 히스토리를 제거하여 대기열의 스토리지를 크게 줄이므로 코드가 메모리 부족을 방지하기 위해 해야하는 어리석은 가지 치기가 거의 줄어 듭니다. 또한 대기열에 복사하는 것이 훨씬 적기 때문에 훨씬 빠르게 실행됩니다.

이제 다양한 보드 상태에 대한 간단한 광범위한 검색입니다. 또한 밝혀진 바와 같이 키 세트 (현재베이스 9의 숫자 세트로 저장되어 있으며, 각각 BS : :: 키로 보드의 기본 9 표현으로 계산 됨)를 비트 세트로 변경하고 싶습니다. 9 개! 비트는 불필요 해 보입니다. "요소 수 시스템"에서 키를 계산하는 방법을 찾았지만 테스트 / 토글하기 위해 비트 세트의 비트를 계산하는 데 사용될 수있었습니다.

따라서 최신 솔루션은 다음과 같습니다.

#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
코드는 C 대신 C ++처럼 보입니다.
user12205

@ace, 실제로 수정되었습니다.
DreamWarrior

경우 다른 사람에 문제를, 내가의 모든 인스턴스를 변경했다 ++ g으로이 컴파일이 int[]에를 const int[]하고 플래그를 설정을 -fpermissive.
Dennis

@Dennis, 죄송합니다. 두 개의 개별 g ++ 컴파일러로 컴파일했지만 마음에 들지 않았습니다. 그러나 새롭고 더 엄격한 버전이 어떻게 울리는 지 알 수 있습니다. 감사.
DreamWarrior

지금은 잘 컴파일되고 훨씬 빠릅니다. 질문에서 삭제 한 주석을 해결하는 방법 : 11 단계가 필요한 순열이 있습니다. 978654321이 그 중 하나입니다.
Dennis

1

CJam-39

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

다른 임의의 솔버 :)
492357816과 같은 문자열을 가져 와서 0에서 3까지의 일련의 숫자를 출력합니다. 각 숫자는 블록의 시계 방향 회전을 나타냅니다 .0 = 왼쪽 위, 1 = 오른쪽 위, 2 = 아래쪽 -왼쪽, 3 = 아래 오른쪽.

간단한 설명 :

4mr
_1>+1보다 크면 0에서 3까지의 난수를 생성합니다 .1보다 크면 숫자가 증가합니다 (따라서 0, 1, 3 또는 4로 끝납니다-4 블록의 시작 색인)
m<. 문자열을 왼쪽으로 회전시킵니다 (예 : 492357816-> 923578164 (블록 회전이 아님)) 블록을 첫 번째 위치에서 회전 시키려면 12345-
[Z0Y4X]\f=> 41352와 같은 처음 5 자에 영향을주는 블록 회전을 수행합니다.
X = 1, Y = 2, Z = 3이므로 [Z0Y4X]는 실제로 [3 0 2 4 1]이며 회전 된 타일의 0 기반 인덱스
5>는 나머지 문자열을 복사
m>합니다 (문자열) 오른쪽
__$>은 문자열이 정렬되어 있는지 확인합니다 (중지 조건입니다)


1

매스 매 티카, 104 자

순열 그룹의 언어로 작업을 해석 할 수 있습니다. 4 개의 회전은 대칭 그룹 S 9 를 생성하는 단지 4 개의 순열 이며, 임무는 생성기의 곱으로서 순열을 작성하는 것이다. Mathematica에는이를위한 기능이 내장되어 있습니다.

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

예:

입력:

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

산출:

{-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: 왼쪽 위 시계 방향
  • 2: 오른쪽 위 시계 방향
  • 3: 오른쪽 아래 시계 방향
  • 4: 왼쪽 아래로 시계 방향
  • -1: 왼쪽 위 반 시계 방향
  • -2: 시계 반대 방향으로 오른쪽 위
  • -3: 오른쪽 아래 반 시계 방향
  • -4: 왼쪽 하단 시계 반대 방향
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.