vpython と sf によるルービック・キューブ

Rubik's Cube を例題に sf と vpython の機能の素晴らしさを示します。

  1. vPython のグラフィック機能を使って Rubik's Cube の内部構造を表現します。
  2. vPython を使って Rubik's Cube ゲームのシミュレーション・プログラムを作ります。vPython のおかげで、グラフィックス処理のプログラムは 150 行程度で済んでしまっています。
  3. Rubik's Cube の面の回転操作はは群演算と見なせます。その群は S48 対称群の部分群です。ルービック・キューブの六面それぞれを回転する操作は 48 x 48 の置換行列で表現できます。この置換行列を sf のファイル変数として表現します。抽象的な記号ではなく、sf を使って実際に計算させられる行列ファイル変数を作ります。

ルービック・キューブの内部構造

Rubik's Cube の内部構造がどのようになっているのか不思議に思う方が多いはずです。普通のジョイント機構の組み合わせでは Rubik's Cube のように縦の面に属して回転していた小さな立方体要素が、次の機会には横の面に属して回転し始めるなんて芸当はできません。ジョイントのどこかで捩れが発生してしまいます。どんな内部構造によって Rubik's Cube の動きを実現しているかも結構面白いパズルです。

Rubik's Cube のように小さな立方体要素を縦にも横にも回転させられる理由は、分割した球殻を六面体の各面の中心部分にある小さな立方体が押さえつけておき、その分割された球殻に中心部分以外の小さな立方体要素が一体成型されているからです。

上のような言葉だけによる説明では分からないと思います。図で示すべきです。でも立体図形で示さねばなりません。Word 程度のお絵描きソフトでは手間が掛かりすぎて書く気になれないでしょう。高価な CAD ソフトを持ち出せば書けますが、それでも二の足を踏む面倒さです。実際にインターネットで調べてみても Rubik's Cube の内部構造を一目で分からせる CAD 図は見つけられませんでした。Web で Rubik's Cube の内部構造を説明しているものの大部分は Rubik's Cube の実物を分解した写真ばかりです。残念ながら、写真だと、先の分割された球殻部分が分かりにくくなります。

でも無償で配布されている vpython を使えば、Rubik's Cube の内部構造を簡単に示すことができます。絵心など必要ありません。プログラムで描かせるのですから、寸法配置データを vPython プログラムで図形化してやりさえすれば三次元俯瞰図として表現できます。

面芯要素を描きます

先に六面体の各面の中心部分のある小さな立方体が、内部の球殻押さえつけると書きました。これを三次元の俯瞰図として示します

上の立体図を描かせた python コードを下に示します。Rubik's Cube の各立方体要素の一辺の長さを 10mm と仮定し、他の立方体要素を押さえ込む面芯部分の構造を vPython で描かせました。Python を使える方は下のプログラムを走らせて三次元図形をマウスで回転させて、自分の手と目で三次元構造を確認してください

//@@
# coding=shift_jis
#06.12.07 Rubik's Cube inner structure
from visual import *
grayGb = (0.5, 0.5, 0.5)

class ClFaceCenterElmnt:
    def __init__(self, framAg, colorAg):
        box( size =( 5, 9, 9), color = colorAg
                , pos = (-12.5,0,0), frame = framAg )
        cylinder(pos = (-10,0,0), axis=(10,0,0), radius = 2.5
                , color = grayGb, frame = framAg)
        curve( radius = 1, pos = [ 
                           (      -10,       -5,        5 ),
                           ( -10.5802, -3.17406,  5.29009 ),
                           ( -10.9109, -1.09109,  5.45545 ),
                           ( -10.9109,  1.09109,  5.45545 ),
                           ( -10.5802,  3.17406,  5.29009 ),
                           (      -10,        5,        5 ) ], 
                           color = grayGb, frame = framAg )

        curve( radius = 1, pos = [ 
                           (      -10,       -5,        3 ),
                           ( -10.6564, -3.19693,  3.19693 ),
                           ( -11.0371, -1.10371,  3.31114 ),
                           ( -11.0371,  1.10371,  3.31114 ),
                           ( -10.6564,  3.19693,  3.19693 ),
                           (      -10,        5,        3 ) ],
                           color = grayGb, frame = framAg )

        curve( radius = 1, pos = [ 
                           (      -10,       -5,        1 ),
                           ( -10.7026, -3.21078,  1.07026 ),
                           ( -11.1144, -1.11144,  1.11144 ),
                           ( -11.1144,  1.11144,  1.11144 ),
                           ( -10.7026,  3.21078,  1.07026 ),
                           (      -10,        5,        1 ) ],
                           color = grayGb, frame = framAg )

        curve( radius = 1, pos = [ 
                           (      -10,       -5,       -1 ),
                           ( -10.7026, -3.21078, -1.07026 ),
                           ( -11.1144, -1.11144, -1.11144 ),
                           ( -11.1144,  1.11144, -1.11144 ),
                           ( -10.7026,  3.21078, -1.07026 ),
                           (      -10,        5,       -1 ) ],
                           color = grayGb, frame = framAg )

        curve( radius = 1, pos = [ 
                           (      -10,       -5,       -3 ),
                           ( -10.6564, -3.19693, -3.19693 ),
                           ( -11.0371, -1.10371, -3.31114 ),
                           ( -11.0371,  1.10371, -3.31114 ),
                           ( -10.6564,  3.19693, -3.19693 ),
                           (      -10,        5,       -3 ) ],
                           color = grayGb, frame = framAg )

        curve( radius = 1, pos = [ 
                           (      -10,       -5,       -5 ),
                           ( -10.5802, -3.17406, -5.29009 ),
                           ( -10.9109, -1.09109, -5.45545 ),
                           ( -10.9109,  1.09109, -5.45545 ),
                           ( -10.5802,  3.17406, -5.29009 ),
                           (      -10,        5,       -5 ) ],
                           color = grayGb, frame = framAg )

box( size =(10,10,10), color = crayola.cyan)
fAt = frame(pos=(-0,0,0), axis=(1,0,0), up = (0,1,0) )
ClFaceCenterElmnt(fAt, crayola.white)

fAt = frame(pos=(-0,0,0), axis=(-1,0,0), up = (0,-1,0) )
ClFaceCenterElmnt(fAt, crayola.orange)

fAt = frame(pos=(-0,0,0), axis=( 0,-1,0), up = (1,0,0) )
ClFaceCenterElmnt(fAt, crayola.blue)

fAt = frame(pos=(-0,0,0), axis=( 0, 1,0), up = (-1,0,0) )
ClFaceCenterElmnt(fAt, crayola.green)

fAt = frame(pos=(-0,0,0), axis=( 0, 0, 1), up = (0,1,0) )
ClFaceCenterElmnt(fAt, crayola.yellow)

fAt = frame(pos=(-0,0,0), axis=( 0, 0,-1), up = (0,1,0) )
ClFaceCenterElmnt(fAt, crayola.red)

#box( size =( 5,10,10), color = crayola.orange, pos = (+12.5,0,0) )
//@@@

面芯要素:ClFaceCenterElmnt

面芯要素一つを python クラスとして記述してやれば、後の位置や色をモディファイされた五つを記述するのは簡単です。上の python コードでは面芯要素を ClFaceCenterElmnt として描いています。

curve

球殻を押さえ込むための湾曲した四角形部分を表示することは、vpython は苦手です。その対策として湾曲した四角形をパイプを並べて表現しました。curve( radius = 1, pos = [...], ...) の部分です。

curve の配置データ

パイプの配置データは sf で作成しました。 <0,0,0> を中心とし <-10, -5, 5> の位置から <-10,5,5> の位置までの円弧上の六つの位置座標を下のような sf 式で計算させられます

//@@
    x = <-10,-5, 5>
    y = <-10, 5, 5>
    r = !norm(x)
    w = y-x
    N = 5
    <<0,N+1,1 @k|
        u = x + k/N w
        u = r u/!norm(u)
    >>
//@@@
<      -10,       -5,        5 >
< -10.5802, -3.17406,  5.29009 >
< -10.9109, -1.09109,  5.45545 >
< -10.9109,  1.09109,  5.45545 >
< -10.5802,  3.17406,  5.29009 >
<      -10,        5,        5 >

<-10, -5, 3> の位置から <-10,5,3> の位置までの円弧上の位置座標は上の x, y 変数を <-10, -5, 3>, <-10, 5, 3> に置き換えるだけで計算できます。その他の三本のパイプ配置データも、上の x, y 変数パラメータを修正してつくります。

これらのパイプ位置の配置データは python 関数として記述したほうがプログラムを短くできます。でもデバッグ作業が面倒です。上のように sf で配置データを計算させたほうがプログラムの作成が簡単です。

回転する面芯要素

シャフトで接続された各面芯要素は回転します。 その回転の様子を下の python コードで表現しました。vPython frame の up 属性を up = (0,1,0.8) や up = (0.3,1,0) とすることで回転させています。

//@@
# coding=shift_jis
#06.12.07 Rubik's Cube inner structure
from visual import *
#grayGb = (0.5, 0.5, 0.5)
grayGb = (0.8, 0.8, 0.8)

class ClFaceCenterElmnt:
    def __init__(self, framAg, colorAg):
        box( size =( 5, 9, 9), color = colorAg
                , pos = (-12.5,0,0), frame = framAg )
        cylinder(pos = (-10,0,0), axis=(10,0,0), radius = 2.5
                , color = grayGb, frame = framAg)
        curve( radius = 1, pos = [ (      -10,       -5,        5 ),
                           ( -10.5802, -3.17406,  5.29009 ),
                           ( -10.9109, -1.09109,  5.45545 ),
                           ( -10.9109,  1.09109,  5.45545 ),
                           ( -10.5802,  3.17406,  5.29009 ),
                           (      -10,        5,        5 ) ], 
                           color = grayGb, frame = framAg )

        curve( radius = 1, pos = [ (      -10,       -5,        3 ),
                           ( -10.6564, -3.19693,  3.19693 ),
                           ( -11.0371, -1.10371,  3.31114 ),
                           ( -11.0371,  1.10371,  3.31114 ),
                           ( -10.6564,  3.19693,  3.19693 ),
                           (      -10,        5,        3 ) ],
                           color = grayGb, frame = framAg )

        curve( radius = 1, pos = [ (      -10,       -5,        1 ),
                           ( -10.7026, -3.21078,  1.07026 ),
                           ( -11.1144, -1.11144,  1.11144 ),
                           ( -11.1144,  1.11144,  1.11144 ),
                           ( -10.7026,  3.21078,  1.07026 ),
                           (      -10,        5,        1 ) ],
                           color = grayGb, frame = framAg )

        curve( radius = 1, pos = [ (      -10,       -5,       -1 ),
                           ( -10.7026, -3.21078, -1.07026 ),
                           ( -11.1144, -1.11144, -1.11144 ),
                           ( -11.1144,  1.11144, -1.11144 ),
                           ( -10.7026,  3.21078, -1.07026 ),
                           (      -10,        5,       -1 ) ],
                           color = grayGb, frame = framAg )

        curve( radius = 1, pos = [ (      -10,       -5,       -3 ),
                           ( -10.6564, -3.19693, -3.19693 ),
                           ( -11.0371, -1.10371, -3.31114 ),
                           ( -11.0371,  1.10371, -3.31114 ),
                           ( -10.6564,  3.19693, -3.19693 ),
                           (      -10,        5,       -3 ) ],
                           color = grayGb, frame = framAg )

        curve( radius = 1, pos = [ (      -10,       -5,       -5 ),
                           ( -10.5802, -3.17406, -5.29009 ),
                           ( -10.9109, -1.09109, -5.45545 ),
                           ( -10.9109,  1.09109, -5.45545 ),
                           ( -10.5802,  3.17406, -5.29009 ),
                           (      -10,        5,       -5 ) ],
                           color = grayGb, frame = framAg )

box( size =(10,10,10), color = crayola.cyan)
fAt = frame(pos=(-0,0,0), axis=(1,0,0), up = (0,1,0.8) )
ClFaceCenterElmnt(fAt, crayola.white)

fAt = frame(pos=(-0,0,0), axis=(-1,0,0), up = (0,-1,0) )
ClFaceCenterElmnt(fAt, crayola.orange)

fAt = frame(pos=(-0,0,0), axis=( 0,-1,0), up = (1,0,0) )
ClFaceCenterElmnt(fAt, crayola.blue)

fAt = frame(pos=(-0,0,0), axis=( 0, 1,0), up = (-1,0,0) )
ClFaceCenterElmnt(fAt, crayola.green)

fAt = frame(pos=(-0,0,0), axis=( 0, 0, 1), up = (0,1,0) )
ClFaceCenterElmnt(fAt, crayola.yellow)

fAt = frame(pos=(-0,0,0), axis=( 0, 0,-1), up = (0.3,1,0) )
ClFaceCenterElmnt(fAt, crayola.red)
//@@@

内部球殻部分を追加します

マゼンタ色の球殻部分を追加した Rubik's Cube 内部の三次元図形を python に描かせます。sphere( color = crayola.magenta, radius = 11 ) のコードです。球殻が分割されていることは、この球殻面に黒いリング線を描かせることで表現しています。ring(color = darkGrayGb, thickness=0.3, pos = (-3, 0,0), radius = 10.583) などのコードです。

//@@
# coding=shift_jis
#06.12.07 Rubik's Cube inner structure
from visual import *
grayGb = (0.5, 0.5, 0.5)

class ClFaceCenterElmnt:
    def __init__(self, framAg, colorAg):
        box( size =( 5, 9, 9), color = colorAg
                , pos = (-12.5,0,0), frame = framAg )
        cylinder(pos = (-10,0,0), axis=(10,0,0), radius = 2.5
                , color = grayGb, frame = framAg)
        curve( radius = 1, pos = [ (      -10,       -5,        5 ),
                           ( -10.5802, -3.17406,  5.29009 ),
                           ( -10.9109, -1.09109,  5.45545 ),
                           ( -10.9109,  1.09109,  5.45545 ),
                           ( -10.5802,  3.17406,  5.29009 ),
                           (      -10,        5,        5 ) ], 
                           color = grayGb, frame = framAg )

        curve( radius = 1, pos = [ (      -10,       -5,        3 ),
                           ( -10.6564, -3.19693,  3.19693 ),
                           ( -11.0371, -1.10371,  3.31114 ),
                           ( -11.0371,  1.10371,  3.31114 ),
                           ( -10.6564,  3.19693,  3.19693 ),
                           (      -10,        5,        3 ) ],
                           color = grayGb, frame = framAg )

        curve( radius = 1, pos = [ (      -10,       -5,        1 ),
                           ( -10.7026, -3.21078,  1.07026 ),
                           ( -11.1144, -1.11144,  1.11144 ),
                           ( -11.1144,  1.11144,  1.11144 ),
                           ( -10.7026,  3.21078,  1.07026 ),
                           (      -10,        5,        1 ) ],
                           color = grayGb, frame = framAg )

        curve( radius = 1, pos = [ (      -10,       -5,       -1 ),
                           ( -10.7026, -3.21078, -1.07026 ),
                           ( -11.1144, -1.11144, -1.11144 ),
                           ( -11.1144,  1.11144, -1.11144 ),
                           ( -10.7026,  3.21078, -1.07026 ),
                           (      -10,        5,       -1 ) ],
                           color = grayGb, frame = framAg )

        curve( radius = 1, pos = [ (      -10,       -5,       -3 ),
                           ( -10.6564, -3.19693, -3.19693 ),
                           ( -11.0371, -1.10371, -3.31114 ),
                           ( -11.0371,  1.10371, -3.31114 ),
                           ( -10.6564,  3.19693, -3.19693 ),
                           (      -10,        5,       -3 ) ],
                           color = grayGb, frame = framAg )

        curve( radius = 1, pos = [ (      -10,       -5,       -5 ),
                           ( -10.5802, -3.17406, -5.29009 ),
                           ( -10.9109, -1.09109, -5.45545 ),
                           ( -10.9109,  1.09109, -5.45545 ),
                           ( -10.5802,  3.17406, -5.29009 ),
                           (      -10,        5,       -5 ) ],
                           color = grayGb, frame = framAg )

box( size =(10,10,10), color = crayola.cyan)
fAt = frame(pos=(-0,0,0), axis=(1,0,0), up = (0,1,0) )
ClFaceCenterElmnt(fAt, crayola.white)

fAt = frame(pos=(-0,0,0), axis=(-1,0,0), up = (0,-1,0) )
ClFaceCenterElmnt(fAt, crayola.orange)

fAt = frame(pos=(-0,0,0), axis=( 0,-1,0), up = (1,0,0) )
ClFaceCenterElmnt(fAt, crayola.blue)

fAt = frame(pos=(-0,0,0), axis=( 0, 1,0), up = (-1,0,0) )
ClFaceCenterElmnt(fAt, crayola.green)

fAt = frame(pos=(-0,0,0), axis=( 0, 0, 1), up = (0,1,0) )
ClFaceCenterElmnt(fAt, crayola.yellow)

fAt = frame(pos=(-0,0,0), axis=( 0, 0,-1), up = (0,1,0) )
ClFaceCenterElmnt(fAt, crayola.red)

# inner spherical shell
# 内部球殻
sphere( color = crayola.magenta, radius = 11 )

# split inner spherical shell
# 内部球殻の切れ目
#darkGrayGb = (0.3, 0.3, 0.3)
darkGrayGb = (0.0, 0.0, 0.0)
# ring size:!sqrt(11^2 - 3^2) == < 10.583 >
ring(color = darkGrayGb, thickness=0.3, pos = (-3, 0,0), radius = 10.583)
ring(color = darkGrayGb, thickness=0.3, pos = ( 3, 0,0), radius = 10.583)

ring(color = darkGrayGb, thickness=0.3, pos = (0,-3,0), radius = 10.583, axis =(0,1,0) )
ring(color = darkGrayGb, thickness=0.3, pos = (0, 3,0), radius = 10.583, axis =(0,1,0) )

ring(color = darkGrayGb, thickness=0.3, pos = (0,0,-3), radius = 10.583, axis =(0,0,1) )
ring(color = darkGrayGb, thickness=0.3, pos = (0,0, 3), radius = 10.583, axis =(0,0,1) )
//@@@

内部球殻部分と一体成型される立方体を追加します。

内部球殻部分と一体成型される立方体を一つだけ追加した三次元図を python に描かせます。下の python ソースの最後尾に、この立方体を追加するコード:四行があります。

//@@
# coding=shift_jis
#06.12.07 Rubik's Cube inner structure
from visual import *
grayGb = (0.5, 0.5, 0.5)

class ClFaceCenterElmnt:
    def __init__(self, framAg, colorAg):
        box( size =( 5, 9, 9), color = colorAg
                , pos = (-12.5,0,0), frame = framAg )
        cylinder(pos = (-10,0,0), axis=(10,0,0), radius = 2.5
                , color = grayGb, frame = framAg)
        curve( radius = 1, pos = [ (      -10,       -5,        5 ),
                           ( -10.5802, -3.17406,  5.29009 ),
                           ( -10.9109, -1.09109,  5.45545 ),
                           ( -10.9109,  1.09109,  5.45545 ),
                           ( -10.5802,  3.17406,  5.29009 ),
                           (      -10,        5,        5 ) ], 
                           color = grayGb, frame = framAg )

        curve( radius = 1, pos = [ (      -10,       -5,        3 ),
                           ( -10.6564, -3.19693,  3.19693 ),
                           ( -11.0371, -1.10371,  3.31114 ),
                           ( -11.0371,  1.10371,  3.31114 ),
                           ( -10.6564,  3.19693,  3.19693 ),
                           (      -10,        5,        3 ) ],
                           color = grayGb, frame = framAg )

        curve( radius = 1, pos = [ (      -10,       -5,        1 ),
                           ( -10.7026, -3.21078,  1.07026 ),
                           ( -11.1144, -1.11144,  1.11144 ),
                           ( -11.1144,  1.11144,  1.11144 ),
                           ( -10.7026,  3.21078,  1.07026 ),
                           (      -10,        5,        1 ) ],
                           color = grayGb, frame = framAg )

        curve( radius = 1, pos = [ (      -10,       -5,       -1 ),
                           ( -10.7026, -3.21078, -1.07026 ),
                           ( -11.1144, -1.11144, -1.11144 ),
                           ( -11.1144,  1.11144, -1.11144 ),
                           ( -10.7026,  3.21078, -1.07026 ),
                           (      -10,        5,       -1 ) ],
                           color = grayGb, frame = framAg )

        curve( radius = 1, pos = [ (      -10,       -5,       -3 ),
                           ( -10.6564, -3.19693, -3.19693 ),
                           ( -11.0371, -1.10371, -3.31114 ),
                           ( -11.0371,  1.10371, -3.31114 ),
                           ( -10.6564,  3.19693, -3.19693 ),
                           (      -10,        5,       -3 ) ],
                           color = grayGb, frame = framAg )

        curve( radius = 1, pos = [ (      -10,       -5,       -5 ),
                           ( -10.5802, -3.17406, -5.29009 ),
                           ( -10.9109, -1.09109, -5.45545 ),
                           ( -10.9109,  1.09109, -5.45545 ),
                           ( -10.5802,  3.17406, -5.29009 ),
                           (      -10,        5,       -5 ) ],
                           color = grayGb, frame = framAg )

box( size =(10,10,10), color = crayola.cyan)
fAt = frame(pos=(-0,0,0), axis=(1,0,0), up = (0,1,0) )
ClFaceCenterElmnt(fAt, crayola.white)

fAt = frame(pos=(-0,0,0), axis=(-1,0,0), up = (0,-1,0) )
ClFaceCenterElmnt(fAt, crayola.orange)

fAt = frame(pos=(-0,0,0), axis=( 0,-1,0), up = (1,0,0) )
ClFaceCenterElmnt(fAt, crayola.blue)

fAt = frame(pos=(-0,0,0), axis=( 0, 1,0), up = (-1,0,0) )
ClFaceCenterElmnt(fAt, crayola.green)

fAt = frame(pos=(-0,0,0), axis=( 0, 0, 1), up = (0,1,0) )
ClFaceCenterElmnt(fAt, crayola.yellow)

fAt = frame(pos=(-0,0,0), axis=( 0, 0,-1), up = (0,1,0) )
ClFaceCenterElmnt(fAt, crayola.red)

# inner spherical shell
# 内部球殻
sphere( color = crayola.magenta, radius = 11 )

# split inner spherical shell
# 内部球殻の切れ目
#darkGrayGb = (0.3, 0.3, 0.3)
darkGrayGb = (0.0, 0.0, 0.0)
# ring size:!sqrt(11^2 - 3^2) == < 10.583 >
ring(color = darkGrayGb, thickness=0.3, pos = (-3, 0,0), radius = 10.583)
ring(color = darkGrayGb, thickness=0.3, pos = ( 3, 0,0), radius = 10.583)

ring(color = darkGrayGb, thickness=0.3, pos = (0,-3,0), radius = 10.583, axis =(0,1,0) )
ring(color = darkGrayGb, thickness=0.3, pos = (0, 3,0), radius = 10.583, axis =(0,1,0) )

ring(color = darkGrayGb, thickness=0.3, pos = (0,0,-3), radius = 10.583, axis =(0,0,1) )
ring(color = darkGrayGb, thickness=0.3, pos = (0,0, 3), radius = 10.583, axis =(0,0,1) )

# add a edge cubic element
# 辺の四角要素を一つ追加する
fAt = frame(pos=(-14.5,14.5,0), axis=(0,-1,0), up = (1,0,0) )
box(frame=fAt, pos = ( 0,4.5, 0),size=(1, 9, 9), color = crayola.blue) # 11th element
box(frame=fAt, pos = (4.5,  0, 0),size=(9, 1, 9), color = crayola.white) # 
box(frame=fAt, pos = (4.5,4.5, 0),size=(9, 9, 5), color = crayola.magenta ) # 
//@@@

これらの Rubik's Cube の内部三次元構造図を見ることで、Rubik's Cube が縦横どちらにも回転できる理由が解るはずです。プログラム素養のあるかたには、これらの図をお絵かきソフトで描くより、vPython で描かせたほうが楽でしょう。

vpython によるルービック・キューブのシミュレーション・プログラム

vPython によるルービック・キューブのシミュレーション・プログラムです。正月のゲームとしても遊べる玩具プログラムになるはずです。

ルービック・キューブのシミュレーション・プログラムのダウンロード
下の四つのプログラムを同一フォルダにダウンロードして Rubik.exe をダブル・クリックするなどで走らせるだけです。それ以外のインストール作業はありません。レジストリを書き換えるなど OS を汚すことはありません。アンインストールは、その四つのファイルを消すだけです。 sf ver 1.2b のダウンロード zip ファイル sf12b.zip
  1. sf ver 1.2b のダウンロード zip ファイル sf12b.zip
  2. Rubik.exe
  3. w9xpopen.exe
  4. w9xpopen.exe
  5. MSVCR71.dll
  6. Rubik.txt
ルービック・キューブ・プログラムの動かし方

Rubik.exe をダブル・クリックするなどで起動するとコンソール・ウィンドウとルービック・キューブが描かれたウィンドウが現れます。

  1. ルービック・キューブが描かれたウィンドウを選択しておいて 橙:orange, 赤:red, 白:white, 青:blue, 黄:yellow, 緑: green の面に対応する o, r, w, b, y, g キーを押すことで、六面の対応する色の面が右回りに回転します。
  2. O, R, W, B, Y, G を押すことで、六面の対応する色の面が左回りに回転します
  3. F1 ファンクション・キーを押すことで、一つ手前の状態に戻すアンデュー操作になります
  4. ルービック・キューブ画面上をマウスの右ボタンを押しながら上下左右にドラッグすることで、ルービックの向きを回転させられます。隙間から見える分だけでは反対側の配置が判らないときの対策などに使ってください。
  5. ルービック・キューブの周囲の黒が多すぎると感じたときは、マウスのセンター釦を押したまま上下にドラッグすることでルービック・キューブの画像を拡大/縮小できます。
  6. このプログラムを終了するには右上にある × 記号をクリックします。
Rubik.txt を使ったマクロの動かし方
インターネット上に解説されているルービック・キューブの解法を調べていくと、決まりきった一連の手順が何度も出てくることに気づくはずです。そして何度も同じ操作を繰り返すことが嫌になってきます。そのために Rubik.txt に一連の操作を書いておきマクロとして実行させることを可能にしています。ダウンロードした Rubik.txt は下のような内容になっています。
# test file
WO
ow
-f:FBFB
   fbfb
-o:
WWBB
bbww
この Rubik.txt を例にマクロの記述の仕方を説明します。
  1. # で始まる行はコメント行です。コンピュータは読み飛ばします。
  2. マクロ動作に入るには t キーを押します。t: text キーが押されると Rubik.exe があるのと同じフォルダーにある Rubik.txt にマクロを実行するモードに移ります。
  3. "WO" など一連の操作を一行に o r w b y g O R W B Y G の文字列の組み合わせで記述します。ただし右端側から実行されます。"WO" が書かれていると Orange 面を左回しにしたあと White 面を左回しにします。右側から実行させているのは、後で説明する sf での行列表現に対応させるためです。
  4. 複数行を書いておけます。複数行あるときは、次の実行行をコンソール・ウインドウに表示した後 s キーが押されるのを待っています。s: step キーが押されると次の行のマクロを実行します。s キー以外は受け付けなくなります。
  5. これらの操作はコンソール・ウィンドウに表示されます。
  6. -f: を書くことによって o r w b y g O R W B Y G の代わりに F B U D L R f b u d l r 文字を使うモードになります。 これらの文字は F:Front B:Back U:Up D:Down L:left R:Right を意味します。群論を使ってルービック・キューブの解法を論ずる数学の世界では F B U D L R が使われることが多いので -f: での切り替えを設けました。
  7. -o: を書くことで F B U D L R f b u d l r 文字を使うモードから o r w b y g O R W B Y G 文字を使うモードに戻ります。
  8. -o: を明示的に使わなくても、Rubik.txt を使うマクロモードを抜けると、自動的に o r w b y g O R W B Y G 文字を使うモードに戻ります。
  9. すべてのマクロを実行し終わると "We detect Rubit.txt file end" をコンソール・ウィンドウに表示します。このあとは以前の o r w b y g O R W B Y G キーを受け付けるようになります。
  10. Rubik.txt ファイルの書き換えは notepad など、普段お使いのエディタで行ってください。

ルービック・キューブ・プログラムの python コード

python2.4 以上と vpython をインストール済みの方は、上の大きなファイルではなく下のパイソン・コードを実行してください。また気に食わない箇所を自由に改変ください。小さなプログラムでありコードを追うことも容易です。vpython を使っているためグラフィック処理部分が極端に単純になっています。
# 06.12.30
# coding=shift_jis
# -F opetion make operation by F,B, U,D, L,R 
# 06.12.10 chage faces to box
#rubik.py for visual python,  by Anton Vredegoor 
# add 'O', 'R', 'W', 'B', 'Y', 'G' reverse rotation key
# (anton@vredegoor.doge.nl) june 2002
"""
    Draws a rubiks cube simulation, press keys "o,r,w,b,y,g"
    to rotate the side which centersquare's color is
    "orange,red,white,blue,yellow,green" in a clockwise
    direction. Clockwise is defined seen from a camera in front
    of the side. For counterclockwise turns repeat a turn three times.
    "f1" undoes the last turn.
    
    Hold down both mouse buttons to zoom (or the middle button or "wheel").
    The right mouse button is used to rotate the camera.
   
    License: BSD-style.
    Dependencies: python, visual python.
    
    This script has been tested with Visual-2002-06-14 on windows 2000 
    and windows 98 SE using python 2.2 .
    Use "idle for visual python" to run it, the standard idle exits after
    the program ends. For visual python see :
    .
"""

from visual import *
from copy import copy

def distribute(n):
    # return cublets position scale vector
    #scale square boundaries between -1 and +1
    #m = n/2.03
    #m = n
    #return [ 3*(i - m)/m  for i in range(n+1)]
    return [0,1,2,3]

def drawsquare(i,j,side,clr,n,ds):
    #draw square i,j at side 'side' with color  'clr',n_sized cube,ds_defined squares
    # side is 0--6 value; o:0, r:1, w:2, b:3, y:4, g:5
    dim, sign = divmod(side,2)
    e = n
    if sign:
        e = 0
    a = [i,j]
    edgeLengthAt = 0.8
    sizeAt = [edgeLengthAt, edgeLengthAt]
    a.insert(dim,e)
    sizeAt.insert(dim,0.1)

    posAt = [ds[a[0]],ds[a[1]],ds[a[2]]]
    for i, edgeAt in enumerate(sizeAt):
        if edgeAt == edgeLengthAt:
            posAt[i] += 0.5
    #print "debug posAt:", posAt, "  side:", side
    return box(pos = posAt, size = sizeAt, color = clr)
    """
    a,b,c,d = [i,j],[i+1,j],[i+1,j+1],[i,j+1]
    if side in [1,2,5]:
         a,c=c,a
    for x in [a,b,c,d]:
        x.insert(dim,e)
    #import pdb; pdb.set_trace()
    #print "debug:[a,b,c,a,c,d]:", [a,b,c,a,c,d]
    #print "debug:clr:", clr
    # a and others are list example:[3, 0, 0]
    posAt = [(ds[x],ds[y],ds[z]) for (x,y,z) in [a,b,c,a,c,d]]
    f = faces(pos= posAt,
                  color=[clr]*len(posAt), normal=[[1,1,1]]*len(posAt) )
    posAt = [(ds[x],ds[y],ds[z]) for (x,y,z) in [a,b,c,d,a]]
    curve(pos= posAt,
             color = (0.5,0.5,0.5), radius = 0.01)
    #curve(pos= posAt,radius = 0.01)
    return f
    """

def initcube(n):
    """
    scene = display(title= "Rubik's Cube",
    display(title= "Rubik's Cube",
         width=400, height=300, range=(5,5,5),
         center=(1.5, 1.5, 1.5)                 , background=(0,0.5,0.5)
    )
    """

    # scene.lights.append(vector([0.7,0,0)) とできない。理由は不明。
    # そのため下のように無理やりな設定を行っている
    lstAt= []
    lstAt.append(vector([0.4, 0,0]))    # Right 面を照らすため
    lstAt.append(vector([-0.7, 0,0]))   # Left  面を照らすため
    lstAt.append(vector([0, 0,  0.7]))  # Front 面を照らすため
    lstAt.append(vector([0, 0, -0.5]))  # Back  面を照らすため
    lstAt.append(vector([0, -0.5, 0]))  # Bottom 面を照らすため
    lstAt.append(vector([0,  0.7, 0]))  # Up     面を照らすため
    scene.lights = lstAt
    scene.center=(1.5, 1.5, 1.5)
    scene.range=(5,5,5)

    a = arrow( pos=(0,0,0), axis=(1,0,0),  color=crayola.red)
    b = arrow( pos=(0,0,0), axis=(0,1,0),  color=crayola.green)
    c = arrow( pos=(0,0,0), axis=(0,0,1),  color=crayola.blue)

    #initialize global variables and draw the cube
    #scene.range = 3
    scene.title = 'rubiks cube simulation'
    scene.forward = (-1,-1,-1)
    ds = distribute(n)
    # data defines which squares to cycle for a turn
    # for example (1,2,3) is square 1,2 on side 3
    # squares cycle four at a time
    # for example a,b,c,d cycles to d,a,b,c
    # o,r,w,b,y,g represents data for orange,red,white,blue,yellow,green turns
    data={ 'y': (  ((2,2,2), (0,2,0), (0,2,3), (2,2,1)),
                    ((1,2,2), (1,2,0), (1,2,3), (1,2,1)),
                    ((0,2,2), (2,2,0), (2,2,3), (0,2,1)),
                    ((0,0,4), (0,2,4), (2,2,4), (2,0,4)),
                    ((1,0,4), (0,1,4), (1,2,4), (2,1,4))    ),
            'g': (  ((2,0,0), (0,0,2), (0,0,1), (2,0,3)),
                    ((1,0,0), (1,0,2), (1,0,1), (1,0,3)),
                    ((0,0,0), (2,0,2), (2,0,1), (0,0,3)),
                    ((0,0,5), (2,0,5), (2,2,5), (0,2,5)),
                    ((1,0,5), (2,1,5), (1,2,5), (0,1,5))    ),
            'w': (  ((2,2,4), (2,2,1), (0,2,5), (2,0,0)),
                    ((1,2,4), (2,1,1), (1,2,5), (2,1,0)),
                    ((0,2,4), (2,0,1), (2,2,5), (2,2,0)),
                    ((0,0,2), (2,0,2), (2,2,2), (0,2,2)),
                    ((1,0,2), (2,1,2), (1,2,2), (0,1,2))    ),
            'b': (  ((0,2,0), (2,0,5), (0,0,1), (0,0,4)),
                    ((0,1,0), (1,0,5), (0,1,1), (1,0,4)),
                    ((0,0,0), (0,0,5), (0,2,1), (2,0,4)),
                    ((0,0,3), (0,2,3), (2,2,3), (2,0,3)),
                    ((1,0,3), (0,1,3), (1,2,3), (2,1,3))    ),
            'r': (  ((0,2,4), (0,2,3), (0,0,5), (0,0,2)),
                    ((0,1,4), (0,1,3), (0,1,5), (0,1,2)),
                    ((0,0,4), (0,0,3), (0,2,5), (0,2,2)),
                    ((0,0,1), (2,0,1), (2,2,1), (0,2,1)),
                    ((1,0,1), (2,1,1), (1,2,1), (0,1,1))    ),
            'o': (  ((2,0,2), (2,0,5), (2,2,3), (2,2,4)),
                    ((2,1,2), (2,1,5), (2,1,3), (2,1,4)),
                    ((2,2,2), (2,2,5), (2,0,3), (2,0,4)),
                    ((0,2,0), (2,2,0), (2,0,0), (0,0,0)),
                    ((0,1,0), (1,2,0), (2,1,0), (1,0,0))    ) }
    #Colordata in rgb fractions.:  orange,red,white,blue,yellow,green
    colors = [[1.0,0.5,0.0], [0.75,0.0,0.0], [0.95,0.95,0.95],
              [0.0,0.0,0.75], [1.0,0.9,0.0], [0.0,0.70,0.0]]
    #draw all squares of the cube, save frame info in _cube
    cube = [[[drawsquare(i,j,k,colors[k],n,ds)\
                # k:6 means six faces on regular hexahedron cube 
                for k in range(6)]\
                    # each face on hexahedron has n x n facet elements
                    for j in range(n)]\
                        for i in range(n)]
    return data,cube

def rorc(turn, blUpperAg, ldata, lcube):
    #rotate a side by 90 degrees, cycle squares in groups of 4
    #import pdb; pdb.set_trace()

    for a,b,c,d in ldata[turn]:
        ai,aj,ak = a
        bi,bj,bk = b
        ci,cj,ck = c
        di,dj,dk = d
        tmp = copy(lcube[di][dj][dk].color)
        lcube[di][dj][dk].color = lcube[ci][cj][ck].color
        lcube[ci][cj][ck].color = lcube[bi][bj][bk].color
        lcube[bi][bj][bk].color = lcube[ai][aj][ak].color
        lcube[ai][aj][ak].color = tmp

        if blUpperAg == True:
            tmp = copy(lcube[di][dj][dk].color)
            lcube[di][dj][dk].color = lcube[ci][cj][ck].color
            lcube[ci][cj][ck].color = lcube[bi][bj][bk].color
            lcube[bi][bj][bk].color = lcube[ai][aj][ak].color
            lcube[ai][aj][ak].color = tmp

            tmp = copy(lcube[di][dj][dk].color)
            lcube[di][dj][dk].color = lcube[ci][cj][ck].color
            lcube[ci][cj][ck].color = lcube[bi][bj][bk].color
            lcube[bi][bj][bk].color = lcube[ai][aj][ak].color
            lcube[ai][aj][ak].color = tmp

class ClKeyIn:
    def __init__(self):
        self.m_blArgF = False
        self.m_data = None
        self.m_cube = None
    def setArgF(self, blAg = True):
        self.m_blArgF = blAg

    def getKeys(self):
        if scene.kb.keys: # is there an event waiting to be processed?
            s = scene.kb.getkey() # obtain keyboard information
            #print "debug s:", s
            if len(s) == 1: 
                if s in ('t', 's', 'T', 'S'):
                    s = s.lower()
                    return s, None
                elif self.m_blArgF == True:
                    dctTableAt = {'f':'o', 'b':'r',  'u':'w', 'd':'b',  'l':'y', 'r':'g'}
                    if s in ('F', 'B', 'U', 'D', 'L', 'R'):
                        return dctTableAt[s.lower()], False
                    elif s in ('f', 'b', 'u', 'd', 'l', 'r'):
                        return dctTableAt[s], True
                    else:
                        #print "debug return None, None"
                        return None, None
                else:
                    if s in ('O', 'R', 'W', 'B', 'Y', 'G'):
                        return s.lower(), True
                    elif s in ('o', 'r', 'w', 'b', 'y', 'g'):
                        return s, False
                    else:
                        return None, None
            elif s == 'f1':
                #print "debug f1:", s
                return 'f1', None
            else:
                return None, None
        else:
            return None, None
            #assert(False)
clKeyInGb = ClKeyIn()

def lrorc(s, blUpperAg):
    rorc(s, blUpperAg, clKeyInGb.m_data, clKeyInGb.m_cube)

def operateByRubikFile():
    fileAt = open('Rubik.txt', 'r')
    dctTableAt = {'f':'o', 'b':'r',  'u':'w', 'd':'b',  'l':'y', 'r':'g'}

    beforeArgFAt = clKeyInGb.m_blArgF
    blFirsOperationAt = True
    lineNumberAt = 0
    for strAt in fileAt:
        lineNumberAt += 1
        strAt = strAt.strip()
        if strAt[0] == '#':
            continue
        elif strAt[0:3] == '-f:':
            clKeyInGb.setArgF(True)
            strAt = strAt[3:]
        elif strAt[0:3] == '-o:':
            clKeyInGb.setArgF(False)
            strAt = strAt[3:]
        
        #import pdb; pdb.set_trace()
        sztAt = strAt.find('#') # delete comment character after '#'
        if not sztAt == -1:     # この条件がないと最後の文字が取り除かれる
            strAt = strAt[0:sztAt]
        strAt = strAt.strip()
        if len(strAt) == 0:
            continue

        print "line:", lineNumberAt, "  :", strAt  #, "  blFirsOperationAt:", blFirsOperationAt
        print "waiting 's' key."
        while ( blFirsOperationAt == False):
            s, blReverseAt = clKeyInGb.getKeys()
            if ( s == 's'):
                break
            else:
                if not s == None:
                    print "You input ", s, " key."
                    print "waiting 's' key."
                continue
        blFirsOperationAt = False
        for chAt in reversed(strAt):
            if (clKeyInGb.m_blArgF == True)   and ( chAt in ('F', 'B', 'U', 'D', 'L', 'R') ):
                blReverseAt = False
                chAt = chAt.lower()
                chAt = dctTableAt[chAt]
            elif (clKeyInGb.m_blArgF == True) and ( chAt in ('f', 'b', 'u', 'd', 'l', 'r') ):
                blReverseAt = True
                chAt = dctTableAt[chAt]
            elif (clKeyInGb.m_blArgF == False) and ( chAt in ('O', 'R', 'W', 'B', 'Y', 'G') ):
                blReverseAt = False
                chAt = chAt.lower()
            elif (clKeyInGb.m_blArgF == False) and ( chAt in ('o', 'r', 'w', 'b', 'y', 'g') ):
                blReverseAt = True
            else:
                continue


            lrorc(chAt, blReverseAt)

    print "We detect Rubit.txt file end"
    clKeyInGb.m_blArgF = beforeArgFAt

def test():
    global clKeyInGb
    clKeyInGb.m_data, clKeyInGb.m_cube = initcube(3)
    #Below code draws 4x4x4 cubiclets but rorc(.) operate only 3x3x3
    #data, cube = initcube(4)

    solve = []
    while 1:
        s, blReverseAt = clKeyInGb.getKeys()
        #print "debug s,blReverseAt:", s, blReverseAt
        if s == None: 
            continue
        elif len(s) == 1: 
            if s == 't':
                operateByRubikFile()
            else:
                if ( s in ('o', 'r', 'w', 'b', 'y', 'g') ):
                    lrorc(s, blReverseAt)
                    solve.append( (s, blReverseAt) )
        elif s == 'f1':
            if solve:
                t, blReverseAt = solve.pop()
                # three forward turns is 1 backward turn ...
                if blReverseAt == True:
                    blReverseAt = False
                else:
                    blReverseAt = True
                lrorc(t, blReverseAt)

if __name__=='__main__':
    import sys
    if len(sys.argv) == 2 and ( sys.argv[1] == "-F" or 
                                sys.argv[1] == "-f" ):
        clKeyInGb.setArgF()
    test()

ルービック・キューブのシミュレーション・プログラムのライセンスについて

このルービック・キューブ・シミュレーション python プログラムは Anton Vredegoor が BSD ライセンスで公開しているものを改変しました。ですから同じ BSD ライセンスとします。無償でお使いください。

ルービック・キューブ群

Rubik's Cube の動きは置換群で表せます。Rubik's Cube を下のように展開してやり、各面に 0, ... 47 の番号を振ってやると、Front 面を右回転させる操作は (05 24,42,15), (06,27,41,12), (07,29,40,10), (16,18,23,21) と (17,20,22,19) の五つの巡回置換を一度に行うことで記述できます。

                     +--------------+
                     |              |
                     |  0    1    2 |
                     |     white:0  |
                     |  3   Up    4 |
                     |              |
                     |  5    6    7 |
                     |              |
      +--------------+--------------+--------------+--------------+
      |              |              |              |              |
      |  8    9   10 | 16   17   18 | 24   25   26 | 32   33   34 |
      |   yellow:1   |   orange:2   |    green :3  |     red:4    |
      | 11  Left  12 | 19 Front  20 | 27 Right  28 | 35  Back  36 |
      |              |              |              |              |
      | 13   14   15 | 21   22   23 | 29   30   31 | 37   38   39 |
      |              |              |              |              |
      +--------------+--------------+--------------+--------------+
                     |              |
                     | 40   41   42 |
                     |     blue:5   |
                     | 43  Down  44 |
                     |              |
                     | 45   46   47 |
                     |              |
                     +--------------+

行列表現と その演算は sf の十八番です。Front 面を右回転させる行列を F とし sf ファイル変数:F.val として下のように構成できます。

F = [[48]]  # Front
F[*,*] = <<1,48,0>>
F[05,24]=1, F[24,42]=1, F[42,15]=1, F[15,05]=1, F:=F
F[05,05]=0, F[24,24]=0, F[42,42]=0, F[15,15]=0, F:=F
F[06,27]=1, F[27,41]=1, F[41,12]=1, F[12,06]=1, F:=F
F[06,06]=0, F[27,27]=0, F[41,41]=0, F[12,12]=0, F:=F
F[07,29]=1, F[29,40]=1, F[40,10]=1, F[10,07]=1, F:=F
F[07,07]=0, F[29,29]=0, F[40,40]=0, F[10,10]=0, F:=F

F[16,18]=1, F[18,23]=1, F[23,21]=1, F[21,16]=1, F:=F
F[16,16]=0, F[18,18]=0, F[23,23]=0, F[21,21]=0, F:=F
F[17,20]=1, F[20,22]=1, F[22,19]=1, F[19,17]=1, F:=F
F[17,17]=0, F[20,20]=0, F[22,22]=0, F[19,19]=0, F:=F

F [[ 48, 48 ]] < 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 > < 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 > < 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 > < 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 > < 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 > < 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 > < 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 > < 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 > < 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 > < 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 > < 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 > < 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 > < 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 > < 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 > < 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 > < 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 > < 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 > < 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 > < 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 > < 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 > < 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 > < 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 > < 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 > < 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 > < 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 > < 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 > < 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 > < 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 > < 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 > < 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 > < 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 > < 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 > < 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 > < 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 > < 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 > < 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 > < 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 > < 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 > < 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 > < 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 > < 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 > < 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 > < 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 > < 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 > < 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 > < 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 > < 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 > < 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 >

F が先の巡回置換になっていることは下のように計算で確認できます

F << 0,48,1>>
<  0,  1,  2,  3,  4, 24, 27, 29,  8,  9,  7, 11,  6, 13, 14,  5, 18, 20, 23, 17, 22, 16, 19, 21, 42, 25, 26, 41, 28, 40, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 10, 12, 15, 43, 44, 45, 46, 47 >
<< 0,48,1>>
<  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47 >

他の B:Back, U:Up, D:Down, L:Left, R:Right 置換行列も同様に、下のように sf ファイル変数として構成できます。

B = [[48]]  # Back
B[*,*] = <<1,48,0>>
B[02,08]=1, B[08,45]=1, B[45,31]=1, B[31,02]=1, B:=B
B[02,02]=0, B[08,08]=0, B[45,45]=0, B[31,31]=0, B:=B
B[01,11]=1, B[11,46]=1, B[46,28]=1, B[28,01]=1, B:=B
B[01,01]=0, B[11,11]=0, B[46,46]=0, B[28,28]=0, B:=B
B[00,13]=1, B[13,47]=1, B[47,26]=1, B[26,00]=1, B:=B
B[00,00]=0, B[13,13]=0, B[47,47]=0, B[26,26]=0, B:=B

B[32,34]=1, B[34,39]=1, B[39,37]=1, B[37,32]=1, B:=B
B[32,32]=0, B[34,34]=0, B[39,39]=0, B[37,37]=0, B:=B
B[33,36]=1, B[36,38]=1, B[38,35]=1, B[35,33]=1, B:=B
B[33,33]=0, B[36,36]=0, B[38,38]=0, B[35,35]=0, B:=B

U = [[48]]  # Up
U[*,*] = <<1,48,0>>
U[26,34]=1, U[18,26]=1, U[10,18]=1, U[34,10]=1, U:=U
U[34,34]=0, U[26,26]=0, U[18,18]=0, U[10,10]=0, U:=U
U[25,33]=1, U[17,25]=1, U[09,17]=1, U[33,09]=1, U:=U
U[33,33]=0, U[25,25]=0, U[17,17]=0, U[09,09]=0, U:=U
U[24,32]=1, U[16,24]=1, U[08,16]=1, U[32,08]=1, U:=U
U[32,32]=0, U[24,24]=0, U[16,16]=0, U[08,08]=0, U:=U

U[00,02]=1, U[02,07]=1, U[07,05]=1, U[05,00]=1, U:=U
U[00,00]=0, U[02,02]=0, U[07,07]=0, U[05,05]=0, U:=U
U[01,04]=1, U[04,06]=1, U[06,03]=1, U[03,01]=1, U:=U
U[01,01]=0, U[04,04]=0, U[06,06]=0, U[03,03]=0, U:=U

D = [[48]]  # Down
D[*,*] = <<1,48,0>>
D[21,29]=1, D[29,37]=1, D[37,13]=1, D[13,21]=1, D:=D
D[21,21]=0, D[29,29]=0, D[37,37]=0, D[13,13]=0, D:=D
D[22,30]=1, D[30,38]=1, D[38,14]=1, D[14,22]=1, D:=D
D[22,22]=0, D[30,30]=0, D[38,38]=0, D[14,14]=0, D:=D
D[23,31]=1, D[31,39]=1, D[39,15]=1, D[15,23]=1, D:=D
D[23,23]=0, D[31,31]=0, D[39,39]=0, D[15,15]=0, D:=D

D[40,42]=1, D[42,47]=1, D[47,45]=1, D[45,40]=1, D:=D
D[40,40]=0, D[42,42]=0, D[47,47]=0, D[45,45]=0, D:=D
D[41,44]=1, D[44,46]=1, D[46,43]=1, D[43,41]=1, D:=D
D[41,41]=0, D[44,44]=0, D[46,46]=0, D[43,43]=0, D:=D

L = [[48]]  # Left
L[*,*] = <<1,48,0>>
L[00,16]=1, L[16,40]=1, L[40,39]=1, L[39,00]=1, L:=L
L[00,00]=0, L[16,16]=0, L[40,40]=0, L[39,39]=0, L:=L
L[03,19]=1, L[19,43]=1, L[43,36]=1, L[36,03]=1, L:=L
L[03,03]=0, L[19,19]=0, L[43,43]=0, L[36,36]=0, L:=L
L[05,21]=1, L[21,45]=1, L[45,34]=1, L[34,05]=1, L:=L
L[05,05]=0, L[21,21]=0, L[45,45]=0, L[34,34]=0, L:=L

L[08,10]=1, L[10,15]=1, L[15,13]=1, L[13,08]=1, L:=L
L[08,08]=0, L[10,10]=0, L[15,15]=0, L[13,13]=0, L:=L
L[09,12]=1, L[12,14]=1, L[14,11]=1, L[11,09]=1, L:=L
L[09,09]=0, L[12,12]=0, L[14,14]=0, L[11,11]=0, L:=L

R = [[48]]  # Right
R[*,*] = <<1,48,0>>
R[32,07]=1, R[47,32]=1, R[23,47]=1, R[07,23]=1, R:=R
R[07,07]=0, R[32,32]=0, R[47,47]=0, R[23,23]=0, R:=R
R[35,04]=1, R[44,35]=1, R[20,44]=1, R[04,20]=1, R:=R
R[04,04]=0, R[35,35]=0, R[44,44]=0, R[20,20]=0, R:=R
R[37,02]=1, R[42,37]=1, R[18,42]=1, R[02,18]=1, R:=R
R[02,02]=0, R[37,37]=0, R[42,42]=0, R[18,18]=0, R:=R

R[26,24]=1, R[31,26]=1, R[29,31]=1, R[24,29]=1, R:=R
R[24,24]=0, R[26,26]=0, R[31,31]=0, R[29,29]=0, R:=R
R[28,25]=1, R[30,28]=1, R[27,30]=1, R[25,27]=1, R:=R
R[25,25]=0, R[28,28]=0, R[30,30]=0, R[27,27]=0, R:=R

Rubik's Cube を解くために、上の位数4の巡回群である F,B,U,D,L,R の積の組み合わせが作る S48 の部分群の性質を活用します。

ルービック・キューブ群の性質

48 x 48 単位行列 E の sf ファイル変数をを下のように作ってやれば、先に定義した F,B,U,D,L,R ファイル変数と組み合わせだ群演算が sf によりコンピュータに計算させられます。これによりルービック・キューブ群の性質を実験的に確認できます。F,B,U,D,L,R を組み合わせた群演算式が理論式であると同時に計算可能な式となります。すなわちコンピュータで F,B,U,D,L,R の組み合わせでできる群の様々の性質を実験的に確認できます。

E = [[48]]
E[*,*] = <<1,48,0>>

E
[[ 48, 48 ]]
< 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 >
< 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 >
< 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 >
< 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 >
< 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 >
< 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 >
< 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 >
< 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 >
< 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 >
< 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 >
< 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 >
< 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 >
< 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 >
< 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 >
< 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 >
< 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 >
< 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 >
< 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 >
< 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 >
< 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 >
< 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 >
< 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 >
< 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 >
< 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 >
< 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 >
< 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 >
< 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 >
< 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 >
< 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 >
< 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 >
< 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 >
< 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 >
< 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 >
< 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 >
< 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 >
< 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 >
< 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 >
< 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 >
< 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0 >
< 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0 >
< 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 >
< 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0 >
< 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 >
< 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0 >
< 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 >
< 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 >
< 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0 >
< 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 >
例えば、F,B,U,D,L,R それぞれが位数 4 の巡回群を構成することは下のように確認できます。
!norm(F^4 - E)
< 0 >
!norm(B^4 - E)
< 0 >
!norm(U^4 - E)
< 0 >
!norm(D^4 - E)
< 0 >
!norm(L^4 - E)
< 0 >
!norm(R^4 - E)
< 0 >

ルービック・キューブ群の部分群

X := R^2 L^2, Y := U^2 D^2, Z := F^2 B^2 と X, Y, Z を作ったとき、X, Y, Z 行列の積の任意の組み合わせの集合は、ルービック・キューブ群の可換な部分群です。

!norm(X^2  - E)
< 0 >
!norm(Y^2  - E)
< 0 >
!norm(Z^2  - E)
< 0 >

知られてるルービック・キューブの解法はすべて、このような見通しやすい部分群の性質を見合わせて解いていきます。

ルービック・キューブの最適解

Rubik's Cube のパズルは与えられた置換状態 permG = (g0,g1,.... g47) に対応する置換行列の F,B,U,D,L,R,F^-1, B^-1,U^-1, D^-1,L^-1,R^-1 による分解を見つけることと言い換えられます。最適解とは、この分解のうちから組み合わせが最小のものを求める問題と言い換えられます。

まだ世界中の誰も この最適解を求める問題を解けていないそうです。もし この問題が解けた方、または解けている方がいましたらお教えください。


ホーム・ページに戻ります