Rubik 's Cube 를 시뮬레이트하려는 경우 측면 당 X 개의 타일 수를 사용하여 큐브의 상태를 메모리에 저장하는 데이터 구조를 어떻게 만들 수 있습니까?
고려해야 할 사항 :
- 큐브는 모든 크기가 될 수 있습니다
- 루빅스 큐브이므로 레이어를 회전 할 수 있습니다
Rubik 's Cube 를 시뮬레이트하려는 경우 측면 당 X 개의 타일 수를 사용하여 큐브의 상태를 메모리에 저장하는 데이터 구조를 어떻게 만들 수 있습니까?
고려해야 할 사항 :
답변:
크기가 평범한 오래된 배열의 문제는 무엇입니까 [6X][X]
? 당신 은 그들을 볼 수 없기 때문에 내부 미니 큐브 에 대해 알 필요 가 없습니다; 그것들은 큐브 상태의 일부가 아닙니다. 보기 좋고 사용하기 쉬운 인터페이스 뒤에 2 개의 추악한 메소드를 숨기고, 유닛 테스트를 통해 죽음까지, 그리고 짜잔!
As long as you know how the six surfaces are "threaded" together
더 강력한 데이터 구조가 제공하는 것입니다. 나는 우리가 같은 것을 주장하고 있다고 생각합니다. 배열 측면과 측면은 블록 배열이지만 "스레딩"을 이해하는 데 도움이되는 측면과 블록에 대한 흥미로운 속성이 많이 있습니다. )
필자는 열렬한 속도 큐브 러이지만 알고리즘 또는 데이터 구조에서 Rubik 큐브를 프로그래밍 방식으로 표현하려고 시도한 적이 없습니다.
큐브에서 각 블록의 고유 한 측면을 캡처하기 위해 별도의 데이터 구조를 만들 것입니다.
큐브에는 3 가지 유형의 블록이 있습니다.
코너 블록-3 개의 컬러면과 3 개의 인접한 조각이있어 언제라도 한면을 공유 할 수 있습니다.
Edge Block-2 개의 컬러 페이스를 가지고 있으며 4 개의 인접한 조각이있어 언제라도 한면을 공유 할 수 있습니다. 3x3 블록에서는 항상 2 개의 중심 조각과 2 개의 모서리 조각이 있습니다.
중앙 블록-3x3 큐브에서이 조각은 움직일 수 없지만 회전 할 수 있습니다. 항상 4 개의 인접 에지 블록이 있습니다. 더 큰 큐브에는 다른 중심 블록 또는 가장자리 조각과 공유 할 수있는 여러 중심 블록이 있습니다. 중앙 블록은 모퉁이 블록에 인접하지 않습니다.
이것을 알면 블록은 다른 블록에 대한 참조 목록을 가질 수 있습니다. 하나의 큐브면을 나타내는 블록 목록과 모든 큐브면에 대한 참조를 유지하는 목록 인 다른 목록 목록을 유지합니다.
모든 큐브면은 고유 한면으로 나타납니다.
이러한 데이터 구조를 사용하면 각면에서 회전 변환을 수행하고 적절한 블록을 적절한 목록으로 이동하거나 제거하는 알고리즘을 작성하는 것이 매우 쉽습니다.
편집 : 중요 사항,이 목록은 물론 주문해야하지만 언급하는 것을 잊었습니다. 예를 들어, 오른쪽을 뒤집 으면 왼쪽 모서리 오른쪽 블록이 오른쪽의 오른쪽 모서리로 이동하고 시계 방향으로 회전합니다.
list of lists
. 아마도 당신이 쿼리 할 수있는 순서없는 블록 목록을 갖는 것이 좋습니다. 변환을 수행 할 때 인접한 블록 참조 만 업데이트하면됩니다. 면의 모든 블록 목록을 원한다면 중앙 블록에 인접한 모든 블록에 대한 목록을 쿼리 할 수 있습니다.
이 문제를 생각할 때 알려진 패턴으로 색상이 움직이는 정적 큐브를 생각합니다. 그래서....
Cube 객체에는 고정 인덱스 0-5로 유지되는 6 개의 Side 객체가 있습니다. 각면에는 고정 인덱스 0-8로 유지되는 9 개의 위치 객체가 있습니다. 각 위치에는 색상이 포함되어 있습니다.
간단하게하기 위해 모든 작업을 1/4 회전 단위로 처리하십시오. 큐브에는 총 6 개의 가능한 동작에 대해 각각 2 개의 가능한 방향으로 3 개의 회전축이 있습니다. 이 정보를 사용하면 큐브에 가능한 6 가지 동작을 매핑하는 것이 매우 간단한 작업이됩니다.
따라서 측면 6, 위치 3의 녹색은 수행되는 작업에 따라 측면 1 위치 3 또는 측면 2 위치 7로 이동할 수 있습니다. 수학 번역을 찾기에 충분하지는 않았지만 코드에서 활용할 수있는 패턴이 나타날 것입니다.
데이터 구조를 사용하여 특정 상태의 특정 큐브를 해결할 수 있는지 어떻게 알 수 있습니까? 나는이 질문으로 스스로 고투하고 있었고 아직 답을 찾지 못했습니다.
이렇게하려면 임의 큐브 상태로 시작하지 마십시오. 대신, 해결 된 상태로 시작하고 프로그래밍 방식으로 n 작업을 수행 하여 큐브를 임의의 시작 상태로 만듭니다. 현재 상태에 도달하기 위해 합법적 인 조치 만 취 했으므로 큐브를 해결할 수 있어야합니다.
xyz 좌표계가 Rubik의 큐브를 처리하는 간단한 방법이고 회전 행렬이 회전을 구현하는 단순하고 일반적인 방법이라는 것을 알았습니다.
위치 벡터를 포함하는 Piece 클래스를 만들었습니다 (x, y, z)
. 회전 행렬을 해당 위치 (행렬-벡터 곱셈)에 적용하여 조각을 회전 할 수 있습니다. 조각은 또한 튜플에서 색상을 추적하여 (cx, cy, cz)
각 축을 따라 색상을 향하게합니다. 적은 양의 논리는 회전 중에 이러한 색상이 적절하게 업데이트되도록합니다. XY 평면에서 90도 회전하면 cx
및 의 값을 교환 할 수 있습니다 cy
.
모든 회전 논리가 Piece 클래스에 캡슐화되어 있기 때문에 Cube는 정렬되지 않은 Piece 목록을 저장할 수 있으며 일반적인 방식으로 회전 할 수 있습니다. 왼쪽면을 회전 시키려면 x 좌표가 -1 인 모든 조각을 선택하고 각 조각에 적절한 회전 행렬을 적용하십시오. 전체 큐브의 회전을 수행하려면 모든 조각에 동일한 회전 행렬을 적용하십시오.
이 구현은 간단하고 몇 가지 장점이 있습니다.
(-1, 1, 1)
)에 0이없고 가장자리의 정확히 하나의 0 ( (1, 0, -1)
)이 있고 가운데 조각에는 두 개의 0 ( (-1, 0, 0)
)이 있습니다.단점 :
간단한 배열 (면의 사각형에 1 대 1의 매핑이있는 각 요소)을 사용하고 특정 순열로 각 회전을 시뮬레이션 할 수 있습니다
3 개의 필수 순열만으로 벗어날 수 있습니다. 앞면을 통해 축으로 슬라이스를 회전하고, 수직 축을 중심으로 큐브를 회전하고 왼쪽과 오른쪽을 통해 큐브를 수평 축으로 회전시킵니다. 다른 모든 움직임은이 세 가지를 연결하여 표현할 수 있습니다.
큐브를 해결할 수 있는지 여부를 가장 간단하게 알 수있는 방법은 위치를 바꾼 두 개의 모서리, 하나의 뒤집힌 모서리, 하나의 뒤집힌 모서리 또는 끝이있는 경우이를 해결하는 것입니다 (입방체를 해결할 일련의 순열을 찾습니다). 당신은 unxolvable 큐브를 가지고 2 스왑 코너
the most straightforward way of know whether a cube is solvable is to solve it
. 글쎄, 당신이 제안하는 모델을 사용하는 것이 사실이라고 생각합니다. 그러나 @maple_shaft에 더 가까운 모델을 사용하고 회전을 추적하는 경우 모서리 플립의 합 mod 2가 0이고 모서리 회전 mod 3이 0인지 확인하여 3x3x3 큐브를 해결할 수 있는지 신속하게 테스트 할 수 있습니다. 그런 다음 순열의 패리티를 확인하십시오. 에지 스왑 및 코너 스왑을 계산할 때 (복구해야 함) 합계 모드 2는 0이어야합니다 (총 패리티 짝수). 큐브가 풀릴 수 있음을 증명하기 위해 필요하고 충분한 테스트입니다.
해결 가능한 첫 번째 조건은 각 조각이 존재하고 각 조각의 색상을 사용하여 "Sovled"큐브를 조립하는 것입니다. 이것은 간단한 점검표로 진실을 결정할 수있는 비교적 사소한 조건입니다. "표준"큐브의 색 구성표 가 정의 되어 있지만 표준 큐브를 다루지 않더라도 6 개만 있습니다! 해결 된면의 가능한 조합.
모든 조각과 색상이 올바르게 설정되면 주어진 물리적 구성을 해결할 수 있는지 여부를 결정하는 것이 중요합니다. 그들 모두가 아닙니다. 이를 확인하는 가장 순진한 방법은 큐브 해결 알고리즘을 실행하고 해결 된 큐브로 종료되는지 확인하는 것입니다. 실제로 큐브를 해결하려고 시도하지 않고 용해도를 결정하는 멋진 조합 기술이 있는지 모르겠습니다.
어떤 데이터 구조에 관해서는 거의 중요하지 않습니다. 까다로운 부분은 변환을 올바르게 수행하고 문헌에서 사용 가능한 알고리즘으로 깔끔하게 작업 할 수있는 방식으로 큐브 상태를 나타낼 수 있다는 것입니다. 메이플 샤프트에 표시된 것처럼 세 가지 유형의 조각이 있습니다. 루빅스 큐브 해결에 관한 문학은 항상 유형별로 조각을 말합니다. 변환도 일반적인 방식으로 표시됩니다 ( Singmaster 표기법 참조 ). 또한 내가 본 모든 솔루션은 항상 한 조각을 참조 점으로 간주합니다 (일반적으로 흰색 중앙 조각을 바닥에 놓음).
이미 큰 답변을 받았으므로 세부 사항을 추가하겠습니다.
구체적인 표현에 관계없이, 렌즈 는 큐브의 다양한 부분에서 "확대"하기위한 매우 훌륭한 도구입니다. 예를 들어, 이 Haskell 코드 의 함수 cycleLeft
를 보십시오 . 길이 4의 모든 목록을 주기적으로 바꾸는 일반적인 함수입니다. L 이동을 수행하는 코드는 다음과 같습니다.
moveL :: Aut (RubiksCube a)
moveL =
cong cube $ cong leftCols cycleLeft
. cong leftSide rotateSideCW
따라서 에 의해 주어진 관점에서cycleLeft
작동 합니다 leftCols
. 마찬가지로 rotateSideCW
회전 된 버전에 영향을 미치는 일반적인 함수 인,는에서 제공 한보기에서 작동합니다 leftSide
. 다른 동작은 유사한 방식으로 구현 될 수 있습니다.
Haskell 라이브러리의 목표는 예쁜 그림을 만드는 것입니다. 나는 그것이 성공했다고 생각한다.
두 가지 별도의 질문을하는 것 같습니다.
- X면이있는 큐브를 나타내는 방법은 무엇입니까?
실제 Rubic의 큐브를 시뮬레이트하려는 경우 모든 Rubik의 큐브에는 6 개의면이 있습니다. 내 말은 "면당 차원 당 X 타일 수"입니다. 원본 Rubic 큐브의 각면은 3x3입니다. 다른 크기로는 4x4 (Professor 's Cube), 5x5 및 6x6이 있습니다.
"표준"큐브 해결 표기법을 사용하여 6면으로 데이터를 표현합니다.
각 변은 X by X의 2 차원 배열입니다.
나는 @maple_shaft의 아이디어가 다른 조각 (미니 큐브)을 다르게 표현하는 것을 좋아합니다 : 중앙, 가장자리 및 모서리 조각은 각각 1, 2 또는 3 색을 나타냅니다.
인접한 조각을 연결하는 가장자리를 가진 (양방향) 그래프로 이들 간의 관계를 나타냅니다. 각 조각에는 가장자리 (연결) 용 슬롯 배열이 있습니다. 중앙 조각에 4 개 슬롯, 가장자리 조각에 4 개 슬롯, 모서리 조각에 3 개 슬롯. 대안 적으로, 중심 조각은 에지 조각에 4 개의 연결부 및 개별적으로 모서리 조각에 대한 4 개의 연결을 가질 수 있고 / 있거나 에지 조각은 중심 조각에 대한 2 개의 연결 및 개별적으로 모서리에 대한 2 개의 연결을 가질 수있다.
이러한 배열은 그래프 가장자리를 반복 할 때 큐브의 회전을 모듈로하여 항상 '동일한'회전을 나타내도록 정렬됩니다. 즉, 예를 들어 중앙 부분의 경우,면이 위로 오도록 큐브를 회전하면 연결 순서는 항상 시계 방향입니다. 가장자리와 모서리 조각도 비슷합니다. 이 속성은 얼굴 회전 후 유지됩니다 (또는 지금 나에게 보입니다).
특정 유형의 조각과 그 방향을 찾는 것이 간단하기 때문에 명확하게 해결할 수없는 조건 (스왑 / 플립 모서리, 스왑 코너)을 쉽게 감지 할 수 있습니다.
노드와 포인터는 어떻습니까?
항상 6 개의면이 있고 1 개의 노드가 1면의 1 정사각형을 나타낸다고 가정합니다.
r , g , b
r , g , b
r , g , b
| | |
r , g , b - r , g , b
r , g , b - r , g , b
r , g , b - r , g , b
노드에는 옆에있는 각 노드에 대한 포인터가 있습니다. 원 회전은 포인터 (노드 수 /면 수) -1 노드 (이 경우 2) 위로 이동합니다. 모든 회전이 원 회전이므로 하나의 rotate
함수 만 작성하면 됩니다. 재귀 적이며 각 노드를 한 공간 이동 한 후 노드 수를 수집하므로 항상 4 개의면이 있으므로 충분히 이동했는지 확인합니다. 그렇지 않은 경우 이동 한 횟수 값을 늘리고 다시 회전하십시오.
이중으로 연결된 것을 잊지 마십시오. 새로 지정된 노드도 업데이트하십시오. 항상 노드 당 하나의 포인터가 업데이트 된 Height * Width 수의 노드가 이동하므로 Height * Width * 2 수의 포인터가 업데이트되어야합니다.
모든 노드가 서로를 가리 키므로 각 노드를 업데이트 할 때 원을 따라 걸어보세요.
이것은 에지 케이스 또는 복잡한 로직없이 모든 크기의 큐브에 적용됩니다. 포인터 걷기 / 업데이트 일뿐입니다.
큐브의 각 회전 부분을 추적하는 세트를 사용하는 개인적인 경험에서 잘 작동합니다. 각 서브 큐브는 3 세트로 루빅 큐브의 크기에 영향을 미치지 않습니다. 따라서 루빅스 큐브에서 하위 세트를 찾으려면 세 세트의 교차점을 가져옵니다 (결과는 하나의 하위 큐브입니다). 이동하려면 영향을받는 서브 큐브를 이동과 관련된 세트에서 제거한 다음 다시 이동의 결과로 가져 오는 세트에 넣습니다.
4 x 4 큐브에는 12 세트가 있습니다. 6 개의면에 6 세트, 큐브 주위에있는 6 개의 밴드에 6 세트. 면에는 각각 16 개의 서브 큐브가 있고 밴드에는 12 개의 서브 큐브가 있습니다. 총 56 개의 서브 큐브가 있습니다. 각 서브 큐브에는 색상 및 색상 방향에 대한 정보가 있습니다. 매직 큐브 자체는 4 x 4 x 4 배열이며 각 요소에는 해당 위치에서 하위 큐브를 정의하는 3 개의 세트로 구성된 정보가 있습니다.
다른 11 개의 답변과 달리이 데이터 구조는 집합의 교차점을 사용하여 큐브의 각 하위 블록 위치를 정의합니다. 이렇게하면 변경시 니어 서브 블록을 업데이트해야하는 작업이 줄어 듭니다.