ルービックキューブをシミュレートしようとしている場合、キューブの状態をメモリに保存するためのデータ構造をどのように作成しますか?
考慮事項:
- キューブは任意のサイズにすることができます
- ルービックキューブなので、レイヤーを回転させることができます
ルービックキューブをシミュレートしようとしている場合、キューブの状態をメモリに保存するためのデータ構造をどのように作成しますか?
考慮事項:
回答:
サイズの単純な古い配列の何が問題になってい[6X][X]
ますか?あなたはそれらを見ないので、あなたは内側のミニキューブについて知る必要はありません。それらはキューブの状態の一部ではありません。見栄えがよく、使いやすいインターフェースの背後に2つのいメソッドを隠し、単体テストで死ぬまで、そして出来上がり!
As long as you know how the six surfaces are "threaded" together
これは、より堅牢なデータ構造が提供するものです。同じことを主張していると思います。配列の側面、およびブロックの配列である側面。ただし、側面とブロックには多くの興味深いプロパティがあります。これは、「スレッド」を理解するのに役立ちます。 )
私は熱心なスピード立方体ですが、アルゴリズムやデータ構造でルービックキューブをプログラムで表現しようとしたことはありません。
キューブ内の各ブロックの固有の側面をキャプチャするために、おそらく個別のデータ構造を作成します。
キューブには3つの異なるタイプのブロックがあります。
コーナーブロック-3つの色の面と、いつでも1つの側面を共有する3つの隣接する部分があります。
エッジブロック-2つのカラーフェースがあり、いつでも1つのサイドを共有する4つの隣接するピースがあります。3x3ブロックでは、常に2つの中央ピースと2つのコーナーピースがあります。
センターブロック-3x3キューブでは、このピースは移動できませんが、回転させることができます。常に4つの隣接するエッジブロックがあります。大きなキューブには、別のセンターブロックまたはエッジピースと共有できる複数のセンターブロックがあります。中央のブロックがコーナーブロックに隣接することはありません。
これを知って、ブロックは、他のブロックへの参照のリストを持つことができます。リストの別のリストを保持します。これは、単一のキューブフェースを表すブロックのリストと、すべてのキューブフェースへの参照を保持するリストです。
すべての立方体の面は、一意の面として表されます。
これらのデータ構造を使用すると、各面で回転変換を実行し、適切なブロックを適切なリストに出し入れするアルゴリズムを簡単に作成できます。
編集:重要な注意、これらのリストはもちろん注文する必要がありますが、私はそれを言及するのを忘れました。 たとえば、右側を反転すると、左隅の右側ブロックが右側の右隅に移動し、時計回りに回転します。
list of lists
。照会できるブロックの順序付けられていないリストを用意する方が良いかもしれません。変換を実行するときに、隣接するブロック参照を更新するだけです。面内のすべてのブロックのリストが必要な場合は、中央ブロックに隣接するすべてのブロックのリストを照会できますか?
この問題を考えると、既知のパターンで色が移動する静的キューブを思い浮かべます。そう....
Cubeオブジェクトには、0〜5のインデックスが付けられたままの6つのSideオブジェクトが含まれます。各側には、インデックス0〜8で固定されたままの9つの位置オブジェクトが含まれます。各位置には色が含まれています。
簡単にするために、すべてのアクションを4分の1回転単位で処理します。回転軸は3つあり、それぞれが2方向になり、キューブで合計6つのアクションが可能です。この情報を使用して、キューブで可能な6つのアクションをマップするのは非常に簡単なタスクになります。
したがって、サイド6、ポジション3の緑色は、特にアクションによって、サイド1ポジション3、またはサイド2ポジション7に移動します。私はこれを数学的翻訳を見つけるのに十分に検討していませんが、おそらくコードで利用できるパターンが現れるでしょう。
データ構造を使用して、特定の状態の特定のキューブが解決可能かどうかを知るにはどうすればよいですか?私はこの質問に自分自身で苦労してきましたが、まだ答えが見つかりませんでした。
これを行うには、ランダムなキューブ状態から始めないでください。代わりに、解決済みの状態から開始し、プログラムでn個のアクションを実行して、キューブをランダムな開始状態にします。現在の状態に到達するために法的措置を講じただけなので、キューブは解決可能でなければなりません。
xyz座標系はルービックキューブをアドレス指定する簡単な方法であり、回転行列は回転を実装する簡単で一般的な方法であることがわかりました。
位置ベクトルを含むPieceクラスを作成しました(x, y, z)
。ピースは、回転マトリックスをその位置に適用することで回転できます(マトリックスとベクトルの乗算)。また、Pieceはタプルで色のトラックを保持し、(cx, cy, cz)
各軸に沿って色を示します。少量のロジックにより、回転中にこれらの色が適切に更新されます。XY平面で90度回転すると、cx
との値が入れ替わりますcy
。
すべての回転ロジックがPieceクラスにカプセル化されているため、CubeはPiecesの順序付けられていないリストを格納でき、回転は一般的な方法で実行できます。左面の回転を行うには、x座標が-1のすべてのピースを選択し、各ピースに適切な回転マトリックスを適用します。キューブ全体の回転を行うには、すべてのピースに同じ回転マトリックスを適用します。
この実装は単純で、いくつかの利点があります。
(-1, 1, 1)
)、エッジにはゼロが1つだけ((1, 0, -1)
)、中央には2つのゼロがあります((-1, 0, 0)
)。欠点:
単純な配列(各要素が面上の正方形に1対1でマッピングされている)を使用して、特定の順列で各回転をシミュレートできます。
必要な順列は3つだけです。前面を通る軸でスライスを回転させ、垂直軸を中心にキューブを回転させ、左右の面で水平軸を中心にキューブを回転させます。他のすべての動きは、これら3つの連結によって表現できます。
キューブが解決可能かどうかを知る最も簡単な方法は、キューブを解決することです(キューブを解決する一連の順列を見つけます)。場所を入れ替えた2つのエッジ、単一の反転エッジ、単一の反転コーナー、またはねじれのないキューブがある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キューブが解けるかどうかをすばやくテストできます。エッジスワップとコーナースワップ(解決に戻るために必要)をカウントする場合、それらの和mod 2は0(パリティの合計が偶数)でなければなりません。これらは、キューブが解けることを証明するために必要かつ十分なテストです。
解決可能な最初の条件は、各ピースが存在し、各ピースの色を使用して「解かれた」キューブを組み立てることです。これは、単純なチェックリストで真実を判断できる比較的些細な条件です。「標準」キューブの配色が定義されていますが、標準キューブを扱っていない場合でも6つしかありません!解決された顔の可能な組み合わせ。
すべての部分と色が正しくなったら、特定の物理構成が解決可能かどうかを判断することが重要です。すべてではありません。これを確認する最も単純な方法は、キューブ解決アルゴリズムを実行し、解決されたキューブで終了するかどうかを確認することです。キューブを実際に解こうとせずに可解性を判断するための派手な組み合わせ手法があるかどうかはわかりません。
どのデータ構造については...それはほとんど問題ではありません。難しい部分は、変換を正しく行い、文献で利用可能なアルゴリズムをきちんと操作できるようにキューブの状態を表すことができることです。メープルシャフトが示したように、3つのタイプの部品があります。ルービックキューブの解法に関する文献では、常にタイプごとのピースを参照しています。変換も一般的な方法で表されます(Singmaster表記を参照)。また、私が見たすべてのソリューションは、常に1つのピースを参照ポイントとして参照します(通常、白い中央のピースを下に置きます)。
すでに素晴らしい回答を受け取っているので、詳細を追加します。
具体的な表現に関係なく、レンズは立方体のさまざまな部分を「ズームイン」するための非常に優れたツールであることに注意してください。たとえばcycleLeft
、このHaskellコードの関数を見てください。これは、長さ4のリストを周期的に並べ替える汎用関数です。L移動を実行するコードは次のようになります。
moveL :: Aut (RubiksCube a)
moveL =
cong cube $ cong leftCols cycleLeft
. cong leftSide rotateSideCW
したがって、はで指定されたビューでcycleLeft
動作します leftCols
。同様にrotateSideCW
、を回転させたバージョンの側面をとる汎用関数であり、で指定されたビューで動作しますleftSide
。他の動きも同様の方法で実装できます。
そのHaskellライブラリの目標は、きれいな写真を作成することです。私はそれが成功したと思う:
あなたは2つの別々の質問をしているようです。
- X個の辺を持つ立方体を表現するには?
実際のルービックキューブをシミュレートする場合、すべてのルービックキューブには6つの側面があります。あなたが言っているのは、「側面ごとの次元ごとのタイルのX数」だと思います。元のルービックキューブの各辺は3x3です。その他のサイズには、4x4(Professor's Cube)、5x5、および6x6が含まれます。
「標準」キューブ解法表記を使用して、データを6面で表します。
各辺は、X x 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)を移行するだけです。すべての回転は円の回転であるため、1つのrotate
関数を作成するだけです。再帰的で、各ノードを1スペースずつ移動し、ノードの数を収集し、常に4つの面があるため、十分に移動したかどうかを確認します。そうでない場合は、値を移動した回数をインクリメントし、再度rotateを呼び出します。
二重にリンクされていることを忘れないでください。したがって、新しくポイントされたノードも更新します。常に移動されるノードのHeight * Width数があり、ノードごとに1つのポインターが更新されるため、更新されるポインターのHeight * Width * 2数が必要です。
すべてのノードがお互いを指しているため、各ノードを更新しながら円を歩き回ってください。
これは、エッジケースや複雑なロジックなしで、あらゆるサイズのキューブで機能するはずです。これは単なるポインタのウォーク/更新です。
キューブの各回転部分を追跡するセットを使用した個人的な経験からうまく機能します。各サブキューブは、ルービックキューブのサイズと同じサイズの3つのセットで構成されています。そのため、ルービックキューブのどこかにサブキューブを見つけるには、3つのセットの交差点を取得します(結果は1つのサブキューブです)。移動を行うには、移動に関係するセットから影響を受けるサブカブスを削除し、移動の結果としてそれらを取得するセットに戻します。
4 x 4キューブには12セットあります。6つの面に6セット、立方体の周りを回る6つのバンドに6セット。面にはそれぞれ16個のサブキューブがあり、バンドにはそれぞれ12個のサブキューブがあります。合計56個のサブキューブがあります。各サブキューブには、色と色の方向に関する情報が保持されます。ルービックキューブ自体は4 x 4 x 4の配列であり、各要素はその場所でサブキューブを定義する3セットで構成される情報を持ちます。
他の11の回答とは異なり、このデータ構造では、セットの共通部分を使用してキューブ内の各サブブロックの場所を定義します。これにより、変更が行われたときに近くのサブブロックを更新する必要がなくなります。