データ構造でルービックキューブを表す方法


58

ルービックキューブをシミュレートしようとしている場合、キューブの状態をメモリに保存するためのデータ構造をどのように作成しますか?

考慮事項:

  • キューブは任意のサイズにすることができます
  • ルービックキューブなので、レイヤーを回転させることができます

2
宿題?または現実の問題
...-sdg

4
そのルービックキューブソルバーのソースコードに興味があるかもしれません。
mouviciel

22
キューブの辺の数は6であるべきだと確信しています
サイモンベルゴ

3
ルービックキューブのモデルを平らにして、すべての側面を同時に見ることができるようになりたいと思います。うーん、私は今それを書きたいと思っています。Tの形でも、可能であれば無限に並べられたものでもかまいません(後者については考えていませんでした)。
リーKowalkowski

6
エリック・エヴァンスは、「モデルは正しいものでも間違ったものでもありません。単純に多かれ少なかれ有用である」と引用したいと思います(引用はおそらく記憶から引用されているように100%正確ではありません)
ピート

回答:


37

サイズの単純な古い配列の何が問題になってい[6X][X]ますか?あなたはそれらを見ないので、あなたは内側のミニキューブについて知る必要はありません。それらはキューブの状態の一部ではありません。見栄えがよく、使いやすいインターフェースの背後に2つのいメソッドを隠し、単体テストで死ぬまで、そして出来上がり!


31
本物のルービックキューブでさえ、内部のミニキューブはありません
jk。

8
これは機能しますが、このような単純なデータ構造に対応するには、アルゴリズムがおそらく非常に複雑になります。
maple_shaft

6
As long as you know how the six surfaces are "threaded" together これは、より堅牢なデータ構造が提供するものです。同じことを主張していると思います。配列の側面、およびブロックの配列である側面。ただし、側面とブロックには多くの興味深いプロパティがあります。これは、「スレッド」を理解するのに役立ちます。 )
maple_shaft

7
@maple_shaft「これは、より堅牢なデータ構造がまさにあなたに与えるものです。」それについては知りません。「構造」が多いデータ構造は、その構造の個々の部分のセットアップ、保守、アクセスに関連する付随的な複雑さを必然的にもたらします。もっと複雑になるものを言うのは難しいです-いくつかのコーナーケースに加えて個々のセルにアクセスする「フリーライド」、またはやや複雑ではないシフトとやや複雑な読み取りのある構造でarrayいシフトを実装します。
-dasblinkenlight

16
ルービックキューブを操作するプログラムを実際に書いた人として、私は6つの2次元配列を1面に1つずつ使用するという単純なアプローチを取りました。少し面倒な基本的な操作をキューブに実装する必要があるのは事実ですが、その後は表現を忘れてしまいます。それは私にとって決して問題ではありませんでした。パフォーマンスの観点からは他の表現がどのように機能するのかとよく疑問に思いましたが、コーディングの観点から負担を感じることはありませんでした。
PeterAllenWebb

39

私は熱心なスピード立方体ですが、アルゴリズムやデータ構造でルービックキューブをプログラムで表現しようとしたことはありません。

キューブ内の各ブロックの固有の側面をキャプチャするために、おそらく個別のデータ構造を作成します。

キューブには3つの異なるタイプのブロックがあります。

  1. コーナーブロック-3つの色の面と、いつでも1つの側面を共有する3つの隣接する部分があります。

  2. エッジブロック-2つのカラーフェースがあり、いつでも1つのサイドを共有する4つの隣接するピースがあります。3x3ブロックでは、常に2つの中央ピースと2つのコーナーピースがあります。

  3. センターブロック-3x3キューブでは、このピースは移動できませんが、回転させることができます。常に4つの隣接するエッジブロックがあります。大きなキューブには、別のセンターブロックまたはエッジピースと共有できる複数のセンターブロックがあります。中央のブロックがコーナーブロックに隣接することはありません。

これを知って、ブロックは、他のブロックへの参照のリストを持つことができます。リストの別のリストを保持します。これは、単一のキューブフェースを表すブロックのリストと、すべてのキューブフェースへの参照を保持するリストです。

すべての立方体の面は、一意の面として表されます。

これらのデータ構造を使用すると、各面で回転変換を実行し、適切なブロックを適切なリストに出し入れするアルゴリズムを簡単に作成できます。

編集:重要な注意、これらのリストはもちろん注文する必要がありますが、私はそれを言及するのを忘れました。 たとえば、右側を反転すると、左隅の右側ブロックが右側の右隅に移動し、時計回りに回転します。


各ブロックには固有のプロパティが必要であることに同意します。ただし、隣接するブロックとの参照を更新する必要があるため、変換は面倒ではありませんlist of lists。照会できるブロックの順序付けられていないリストを用意する方が良いかもしれません。変換を実行するときに、隣接するブロック参照を更新するだけです。面内のすべてのブロックのリストが必要な場合は、中央ブロックに隣接するすべてのブロックのリストを照会できますか?
メル

@Melどちらの方法でも可能ですが、dasblinkenlightと話した後、実際には彼のアプローチはそれほど複雑ではないと思います。彼の答えが私の票よりも多かったことを願っています。私はアルゴリズムがそれほど優れておらず、最も効率的なアルゴリズムではありません。ルービックキューブが好きで、それらを収集しています(世界中から40種類以上)。
maple_shaft

dasblinknenlightの答え私はあなたの答えは、このようなデータ構造のために必要とされるロジックの一部が含ま事実、および異なるブロック属性好きなので、最も簡単な解決策は、私はあなたに恵みを授与しています
レイチェル

このデータモデルは現実により忠実ですが、単純な操作の一部を本来よりも難しくします。キューブの状態を取得するだけで、キューブのリストを再帰的に移動する必要があり、面倒です。
ジブ

@Ziv本当ですが、質問はデータ構造についてであり、必ずしもアルゴリズムについてではありませんでした。
maple_shaft

13

この問題を考えると、既知のパターンで色が移動する静的キューブを思い浮かべます。そう....

Cubeオブジェクトには、0〜5のインデックスが付けられたままの6つのSideオブジェクトが含まれます。各側には、インデックス0〜8で固定されたままの9つの位置オブジェクトが含まれます。各位置には色が含まれています。

簡単にするために、すべてのアクションを4分の1回転単位で処理します。回転軸は3つあり、それぞれが2方向になり、キューブで合計6つのアクションが可能です。この情報を使用して、キューブで可能な6つのアクションをマップするのは非常に簡単なタスクになります。

したがって、サイド6、ポジション3の緑色は、特にアクションによって、サイド1ポジション3、またはサイド2ポジション7に移動します。私はこれを数学的翻訳を見つけるのに十分に検討していませんが、おそらくコードで利用できるパターンが現れるでしょう。

データ構造を使用して、特定の状態の特定のキューブが解決可能かどうかを知るにはどうすればよいですか?私はこの質問に自分自身で苦労してきましたが、まだ答えが見つかりませんでした。

これを行うには、ランダムなキューブ状態から始めないでください。代わりに、解決済みの状態から開始し、プログラムでn個のアクションを実行して、キューブをランダムな開始状態にします。現在の状態に到達するために法的措置を講じただけなので、キューブは解決可能でなければなりません。


1
古典的な「ここから始めたくない」アドバイス!
エルグン

8

xyz座標系はルービックキューブをアドレス指定する簡単な方法であり、回転行列は回転を実装する簡単で一般的な方法であることがわかりました。

位置ベクトルを含むPieceクラスを作成しました(x, y, z)。ピースは、回転マトリックスをその位置に適用することで回転できます(マトリックスとベクトルの乗算)。また、Pieceはタプルで色のトラックを保持し、(cx, cy, cz)各軸に沿って色を示します。少量のロジックにより、回転中にこれらの色が適切に更新されます。XY平面で90度回転すると、cxとの値が入れ替わりますcy

すべての回転ロジックがPieceクラスにカプセル化されているため、CubeはPiecesの順序付けられていないリストを格納でき、回転は一般的な方法で実行できます。左面の回転を行うには、x座標が-1のすべてのピースを選択し、各ピースに適切な回転マトリックスを適用します。キューブ全体の回転を行うには、すべてのピースに同じ回転マトリックスを適用します。

この実装は単純で、いくつかの利点があります。

  1. Pieceオブジェクトの位置は変わりますが、色は変わりません。これは、赤緑のピースを要求し、オブジェクトに固執し、いくつかの回転を行い、同じオブジェクトをチェックして赤緑のピースがどこで終わったかを確認できることを意味します。
  2. ピースの各タイプ(エッジ、センター、コーナー)には、固有の座標パターンがあります。3x3キューブの場合、コーナーピースには位置ベクトルにゼロがなく((-1, 1, 1))、エッジにはゼロが1つだけ((1, 0, -1))、中央には2つのゼロがあります((-1, 0, 0))。
  3. 3x3キューブで機能する回転行列は、NxNキューブで機能します。

欠点:

  1. 行列ベクトルの乗算は、配列の値を交換するよりも時間がかかります。
  2. 位置によるピースの線形時間ルックアップ。ピースを外部データ構造に保存し、位置による一定時間のルックアップのために回転中にそれを更新する必要があります。これは、回転行列の使用の優雅さの一部を無効にし、回転ロジックをCubeクラスにリークします。何らかの検索ベースの解決アルゴリズムを実装していた場合、別の実装を使用します。
  3. (解析中の)パターン分析は、それほど優れたものではありません。ピースには隣接するピースの知識がなく、上記のパフォーマンスの問題のために分析が遅くなります。

3
この種の実装は、3Dグラフィックスプログラムでキューブを表現するのに最適であることがわかりました。行列の乗算により、レイヤーの回転をアニメートできます。このアプローチの実装例については、このgithubリポジトリを参照してください。3Dキューブにソルバーを追加するには、他の回答の1つからのアルゴリズムとデータ構造が必要であることを検討しています。
ジョナサンウィルソン

5

単純な配列(各要素が面上の正方形に1対1でマッピングされている)を使用して、特定の順列で各回転をシミュレートできます。

必要な順列は3つだけです。前面を通る軸でスライスを回転させ、垂直軸を中心にキューブを回転させ、左右の面で水平軸を中心にキューブを回転させます。他のすべての動きは、これら3つの連結によって表現できます。

キューブが解決可能かどうかを知る最も簡単な方法は、キューブを解決することです(キューブを解決する一連の順列を見つけます)。場所を入れ替えた2つのエッジ、単一の反転エッジ、単一の反転コーナー、またはねじれのないキューブがある2つのスワップされたコーナー


1
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(パリティの合計が偶数)でなければなりません。これらは、キューブが解けることを証明するために必要かつ十分なテストです。
-jimhark

3

解決可能な最初の条件は、各ピースが存在し、各ピースの色を使用して「解かれた」キューブを組み立てることです。これは、単純なチェックリストで真実を判断できる比較的些細な条件です。「標準」キューブの配色が定義されていますが、標準キューブを扱っていない場合でも6つしかありません!解決された顔の可能な組み合わせ。

すべての部分と色が正しくなったら、特定の物理構成が解決可能かどうかを判断することが重要です。すべてではありません。これを確認する最も単純な方法は、キューブ解決アルゴリズムを実行し、解決されたキューブで終了するかどうかを確認することです。キューブを実際に解こうとせずに可解性を判断するための派手な組み合わせ手法があるかどうかはわかりません。

どのデータ構造については...それはほとんど問題ではありません。難しい部分は、変換を正しく行い、文献で利用可能なアルゴリズムをきちんと操作できるようにキューブの状態を表すことができることです。メープルシャフトが示したように、3つのタイプの部品があります。ルービックキューブの解法に関する文献では、常にタイプごとのピースを参照しています。変換も一般的な方法で表されます(Singmaster表記を参照)。また、私が見たすべてのソリューションは、常に1つのピースを参照ポイントとして参照します(通常、白い中央のピースを下に置きます)。


1
ポイント2の場合は、ランダムキューブから開始して解決可能かどうかをチェックする代わりに。解決されたキューブから始めて、キューブに対してランダムなアクションをn回実行して、キューブをランダムな状態にします。
マシューヴァインズ

はい、絶対に、それは物理的に解決可能な構成を生成する最も簡単な方法です。任意の構成から始めて、それが解決可能かどうかを判断することは、間違いなく別個ですが関連する問題です。
アンジェロ

2
あなたは、キューブが解決できるものであるかどうかを判断するための「派手なテクニック」があるかもしれないと推測します。実際にあります。キューブを分解し、ステッカーを付けたままキューブを再組み立てする場合、必ずしも解決可能なキューブが得られるとは限りません。実際には、オッズは1〜12ですに対してあなたは解けるキューブを持っています。エッジとコーナーのパリティ分析により、解決可能な状態にあるかどうかを判断できます。実際にキューブを解こうとする必要はありません。
エリックリッパー

1
以下は、キューブを解決可能にするために保持する必要がある3種類のキューブペアプロパティの概要です。ryanheise.com/cube/cube_laws.html
エリックリッパー

1
私は本当に素敵な答えをマッチstackexchangeサイトにその質問を投稿して得た:math.stackexchange.com/questions/127577/...
メル

3

すでに素晴らしい回答を受け取っているので、詳細を追加します。

具体的な表現に関係なく、レンズは立方体のさまざまな部分を「ズームイン」するための非常に優れたツールであることに注意してください。たとえばcycleLeftこのHaskellコードの関数を見てください。これは、長さ4のリストを周期的に並べ替える汎用関数です。L移動を実行するコードは次のようになります。

moveL :: Aut (RubiksCube a)
moveL =
    cong cube $ cong leftCols cycleLeft
              . cong leftSide rotateSideCW

したがって、はで指定されたビューでcycleLeft動作します leftCols。同様にrotateSideCW、を回転させたバージョンの側面をとる汎用関数であり、で指定されたビューで動作しますleftSide。他の動きも同様の方法で実装できます。

そのHaskellライブラリの目標は、きれいな写真を作成することです。私はそれが成功したと思う: 動作中のdiagram-rubiks-cubeライブラリ


2

あなたは2つの別々の質問をしているようです。

  1. X個の辺を持つ立方体を表現するには?

実際のルービックキューブをシミュレートする場合、すべてのルービックキューブには6つの側面があります。あなたが言っているのは、「側面ごとの次元ごとのタイルのX数」だと思います。元のルービックキューブの各辺は3x3です。その他のサイズには、4x4(Professor's Cube)、5x5、および6x6が含まれます。

「標準」キューブ解法表記を使用して、データを6面で表します。

  • FRONT:ソルバーに面している面
  • バック
  • アップ
  • ダウン

各辺は、X x Xの2次元配列です。


17x17キューブを購入できます!いくつかの機械的な妥協がありますが、実物と同形です。
–RBerteig

1

@maple_shaftのアイデアは、異なるピース(ミニキューブ)を異なる方法で表すのが好きです。中央、エッジ、コーナーの各ピースは、それぞれ1、2、または3色を持ちます。

それらの間の関係を、エッジが隣接する部分をつなぐ(双方向)グラフとして表します。各ピースには、エッジ(接続)用のスロットの配列があります:中央ピースに4スロット、エッジピースに4スロット、コーナーピースに3スロット。あるいは、センターピースはエッジピースに4つ、コーナーピースに4つ別々に接続でき、エッジピースはセンターピースに2つ、コーナーピースに2つ別々に接続できます。

これらの配列は、立方体の回転を法として、グラフのエッジを反復処理することが常に「同じ」回転を表すように順序付けられます。つまり、たとえばセンターピースの場合、立方体を回転させてその面が上になるようにすると、接続の順序は常に時計回りになります。エッジピースとコーナーピースについても同様です。このプロパティは、顔の回転後も保持されます(または、今では私には思えます)。

  • エッジに属するピースを見つけるのは簡単です。
  • 顔に属するピースを見つけるのは簡単です。
  • 特定の顔に対して特定の方向にある顔、または反対の顔を見つけるには、明確に定義された2つまたは3つのリンクを移動します。
  • 面を回転させるには、面の中央部分に接続されているすべての部分の接続を更新します。

特定の種類のピースやその向きを見つけるのは簡単なので、できれば簡単に解決できない条件(スワップ/フリップエッジ、スワップコーナー)を検出することもできます。


1

ノードとポインターはどうですか?

常に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数が必要です。

すべてのノードがお互いを指しているため、各ノードを更新しながら円を歩き回ってください。

これは、エッジケースや複雑なロジックなしで、あらゆるサイズのキューブで機能するはずです。これは単なるポインタのウォーク/更新です。


-1

キューブの各回転部分を追跡するセットを使用した個人的な経験からうまく機能します。各サブキューブは、ルービックキューブのサイズと同じサイズの3つのセットで構成されています。そのため、ルービックキューブのどこかにサブキューブを見つけるには、3つのセットの交差点を取得します(結果は1つのサブキューブです)。移動を行うには、移動に関係するセットから影響を受けるサブカブスを削除し、移動の結果としてそれらを取得するセットに戻します。

4 x 4キューブには12セットあります。6つの面に6セット、立方体の周りを回る6つのバンドに6セット。面にはそれぞれ16個のサブキューブがあり、バンドにはそれぞれ12個のサブキューブがあります。合計56個のサブキューブがあります。各サブキューブには、色と色の方向に関する情報が保持されます。ルービックキューブ自体は4 x 4 x 4の配列であり、各要素はその場所でサブキューブを定義する3セットで構成される情報を持ちます。

他の11の回答とは異なり、このデータ構造では、セットの共通部分を使用してキューブ内の各サブブロックの場所を定義します。これにより、変更が行われたときに近くのサブブロックを更新する必要がなくなります。


ポイントが作られ、前11件の答えで説明した上で、これはかなりのものを提供していないようだ
ブヨ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.