ポケットキューブ(2×2×2 のルービックキューブ)をブラウザに実装した。ページを開くとタブの中で全 3,674,160 局面を幅優先でたどり(約 1 秒)、どの局面でも「あと何手か」を面回転(half turn も 1 手)と 90° 回転(half turn は 2 手)の両方で表示する。18 個の回転ボタンにはそれぞれ「押したら何手になるか」が出る。距離の表は Wikipedia(英・日)・OEIS A079761 / A079762・Jaap Scherphuis の 2 メトリクス同時分布(180 セル)とすべて一致し、6・24・48 対称で割った局面数、R と U だけで作る 2 生成元群(29,160 局面、直径 17、対蹠点 18 個)の Bump & Auerbach (2006) の表と対蹠点どうしの距離も一致した。公表値は論文の PDF や wikitext からスクリプトで抜き出して照合していて、手で写した数字は 1 つもない。446 個中 445 個が一致。合わなかった 1 個は Wikipedia の「この群(位数 3,674,160)の元の最大位数は 45。例: U R² L′」で、この群の最大位数は 36 だった。45 は角を 1 つも固定しない 24 倍の群(88,179,840)の値で、例の L′ は固定したはずの角を動かす。ただし実物のキューブで U R² L′ を繰り返すと、持ち替えを許しても 45 回目で初めて揃って見える。「周期 45 の手順」はあるが「位数 45 の元」は 367 万の群にはない。そのからくり(9 回で持ち方が戻り、その 18 手が位数 5)もページで確かめられる。テスト 11 件。ソルバー内蔵パズル第 78 弾。
デモ: https://sen.ltd/portfolio/pocket-cube/
リポジトリ: https://github.com/sen-ltd/pocket-cube
ルールと 2 つの数え方
2×2×2 のキューブは 8 個の角のブロックだけでできている。6 面それぞれを 90°・180°・270° 回せるので、回し方は 18 通り(U, U2, U′, R, …, B′)。全面を 1 色ずつに揃えれば完成。
「何手」の数え方は 2 つある。
| 数え方 | 1 手とは | 最大 | 平均 |
|---|---|---|---|
| 面回転(face turn, HTM) | 1 つの面を 90°・180°・270° のどれか回す | 11 | 8.7556 |
| 90° 回転(quarter turn, QTM) | 1 つの面を 90° 回す(180° は 2 手) | 14 | 10.6664 |
ページではどちらの数え方で Hint / Solve するかを選べる。距離パネルには両方の値と、「この距離にある局面は全体の何 %」が出る。
局面は 3,674,160 個、群は 2 つ
角 8 個の並べ方が 8!、ひねりは 7 個まで自由(合計は 3 の倍数)で 3^7。2×2×2 にはセンターがないので、キューブ全体の持ち方 24 通りは同じ局面とみなして割る。
$$\frac{8! \times 3^7}{24} = 7! \times 3^6 = 3{,}674{,}160$$
コードでは、群を 2 つ区別して持つ。
- G: 18 通りの面回転で作れる状態すべて。8!·3^7 = 88,179,840 個。キューブ全体の持ち替えもこの中に入る(x = R L′ など)
- H: 左下奥の角(DBL)を動かさない状態。U・R・F だけで作れる群で、7!·3^6 = 3,674,160 個
G のどの状態も「H の元 1 つ」×「持ち替え 24 通りのどれか」にちょうど 1 通りに分解できる。だから「持ち方を無視した局面」は H そのものになる。持ち替えで DBL を元の位置・向きに戻せばよい。
/**
* The same position held so that DBL is home: the unique h in H and rotation r
* with a · r = h. Positions "up to how you hold the cube" are exactly H.
*/
export function normalize(a: Cube): { h: Cube; r: number } {
for (let r = 0; r < ROTATIONS.length; r++) {
const h = mul(a, ROTATIONS[r]);
if (inH(h)) return { h, r };
}
throw new Error('unreachable: every state is one of H times a rotation');
}
状態は Kociemba の CubieCube と同じく、スロット i にある角 cp[i] とそのひねり co[i] の組で、積 mul(a, b) は「a をしてから b」。D を回すのは、U を回して全体を持ち替えたのと同じ(D と U は可換で、D = U·y′)。なので距離を調べるには U・R・F の 9 手だけで足りて、D・L・B の 9 手は持ち替えれば U・R・F のどれかになる。テストでは、ランダムな 200 局面すべてで「D, D2, D′, L, … B′ を押した後の距離」の列が「U, U2, U′, R, … F′」の列と一致することを確かめている。
幅優先: 置換表 5,040 × 9 とひねり表 729 × 9
H の局面に 0 〜 3,674,159 の番号を振る。動く 7 個の角の並び(Lehmer 符号で 0 〜 5,039)× 最初の 6 個のひねり(3 進 6 桁で 0 〜 728)。
回転は「角がどこへ行くか」と「ひねりがどう変わるか」を独立に変える。行き先はひねりに依存せず、新しいひねりは古いひねりと回転だけで決まる。なので探索の中ではキューブを掛け算せず、5,040 × 9 と 729 × 9 の 2 つの表を引くだけになる。
export function godsTable(tables: MoveTables, gens: number[]): Uint8Array {
const dist = new Uint8Array(N_H).fill(UNSEEN);
const nm = H_MOVES.length;
const { perm, twist } = tables;
dist[0] = 0;
let found = 1;
for (let d = 0; found > 0; d++) {
found = 0;
for (let i = 0; i < N_H; i++) {
if (dist[i] !== d) continue;
const p = (i / N_TWIST) | 0;
const t = i - p * N_TWIST;
for (const m of gens) {
const j = perm[p * nm + m] * N_TWIST + twist[t * nm + m];
if (dist[j] === UNSEEN) {
dist[j] = d + 1;
found++;
}
}
}
}
return dist;
}
gens に 9 手全部を渡せば面回転、U, U′, R, R′, F, F′ の 6 手なら 90° 回転の距離になる。1 局面 1 バイトで 3.6 MB。2 つの表を両方作っても Node で 1.2 秒、手元の Chromium でも 1.1 秒前後で、ページはこれを開くたびに作っている。
| 距離 | 面回転 | 90° 回転 |
|---|---|---|
| 0 | 1 | 1 |
| 1 | 9 | 6 |
| 2 | 54 | 27 |
| 3 | 321 | 120 |
| 4 | 1,847 | 534 |
| 5 | 9,992 | 2,256 |
| 6 | 50,136 | 8,969 |
| 7 | 227,536 | 33,058 |
| 8 | 870,072 | 114,149 |
| 9 | 1,887,748 | 360,508 |
| 10 | 623,800 | 930,588 |
| 11 | 2,644 | 1,350,852 |
| 12 | 782,536 | |
| 13 | 90,280 | |
| 14 | 276 |
2 列とも OEIS A079761 / A079762、英語版・日本語版 Wikipedia の表と全項一致。Wikipedia の割合の列(a% と q%、30 個)も、ここで出した比をその桁数で丸めた値と全部一致した。
2 つの数え方の同時分布
Jaap Scherphuis のページには、面回転の距離を列、90° 回転の距離を行にした 15 × 12 の表がある。距離の表を 2 つ持っていれば、同時分布は 1 回なめるだけで出る。180 セル(空欄は 0)と行・列の合計 27 個がすべて一致し、平均の 8.7556 / 10.666 も一致した。
90° 回転で最も遠い 276 局面は、面回転では 9 手が 8 個、10 手が 160 個、11 手が 108 個で、面回転でも最遠(11 手)とは限らない。ページのスクリーンショットの局面も、面回転 11 手・90° 回転 13 手。
対称で割る: 48 通りをステッカーの置換で
英語版 Wikipedia にはもう 1 つ、「対称で同じとみなした局面数」の表がある。
- 6 通り: 固定した角(DBL)の位置を保つ対称。対角線まわりの 3 回転 × 鏡映
- 24 通り: キューブの回転すべて
- 48 通り: 回転と鏡映すべて
局面 h と対称 s に対して s·h·s⁻¹(同じ手順を回した・鏡に映した世界でやったもの)を同じとみなす。鏡映は時計回りのひねりを反時計回りに変えるので、(cp, co) の算術では書きにくい。そこで対称は 24 枚のステッカーの場所の置換として作った。ステッカーの中心座標(面の法線方向に ±2、他の 2 軸に ±1)に 48 通りの符号付き置換行列を掛け、行き先のステッカーを探す。
/** s · a · s⁻¹: the same position seen in the frame s. */
export function conjugate(a: Cube, s: Symmetry): Cube {
const w = toStickers(a);
const out = new Array<number>(24);
for (let x = 0; x < 24; x++) out[s.perm[x]] = s.perm[w[x]];
return fromStickers(out);
}
共役した結果は DBL を動かしていることがあるので、normalize で持ち直してから番号を引く。数え方は、番号順に見ていって未見の局面に当たったら、その軌道(高々 48 個)に全部印を付け、類を 1 つ数える。
| 面回転 | 対称なし | 6 | 24 | 48 |
|---|---|---|---|---|
| 0 | 1 | 1 | 1 | 1 |
| 1 | 9 | 2 | 3 | 2 |
| 2 | 54 | 9 | 9 | 5 |
| 3 | 321 | 54 | 36 | 19 |
| 4 | 1,847 | 309 | 132 | 68 |
| 5 | 9,992 | 1,670 | 529 | 271 |
| 6 | 50,136 | 8,361 | 2,276 | 1,148 |
| 7 | 227,536 | 37,943 | 9,768 | 4,915 |
| 8 | 870,072 | 145,046 | 36,582 | 18,364 |
| 9 | 1,887,748 | 314,710 | 79,006 | 39,707 |
| 10 | 623,800 | 104,076 | 26,137 | 13,225 |
| 11 | 2,644 | 449 | 129 | 77 |
| 計 | 3,674,160 | 612,630 | 154,608 | 77,802 |
36 個と合計 4 個がすべて一致(3 種類で 20.8 秒)。テストでは、48 通りの共役が 18 手の集合をそれ自身に写すこと(鏡映は U を U′ 系に写す)と、共役しても距離が変わらないことも確かめている。
R と U だけの群: Bump & Auerbach (2006)
Wikipedia が引いている Daniel Bump と Daniel Auerbach の論文 "Unravelling the (miniature) Rubik's Cube through its Cayley Graph"(2006)は、R と U だけで作る群を調べている。元の URL は消えていて、Wayback Machine の PDF から数字を抜いた。
- 位数 29,160、直径 17(R, R′, U, U′ を 1 手とする)
- 距離ごとの局面数 1, 4, 10, 24, 58, 130, 271, 526, 980, 1750, 2731, 3905, 5229, 5848, 4792, 2375, 508, 18
- 距離 17 の 18 個(対蹠点)の手順 A1 〜 A18
- 対蹠点は 4 つの塊に分かれ、塊の中では 6 か 8 離れていて、名指しされた 8 組は 10 または 12、塊どうしは最低 10
同じ godsTable に R, R′, U, U′ の 4 手を渡すだけで、位数・直径・18 項すべてが一致した。18 個の手順はどれも距離 17 で、距離 17 の局面はこの 18 個で全部だった。
対蹠点どうしの距離は、最初は 8 個が合わなかった。名指しの 10 の 2 組が 12 になり、塊どうしの最小が 6 になる。原因は積の向き。論文の群は関数の合成と同じく右から左に掛けていて、ケイリーグラフの距離 x⁻¹y は、手順を左から右に読むこのコードでは x·y⁻¹ になる。そう読むと、名指しの 8 組、その他の塊内の 24 組、塊どうしの最小 10 がすべて一致した。PDF のテキストでは R′ が「R0」、U2 が「U 2」になっていたので、抽出スクリプトで戻している。
本題: 「この群の最大位数は 45」
英語版 Wikipedia の Pocket Cube の記事には、局面数の式のすぐ後にこうある。
7!·3⁶ = 3,674,160. This is the order of the group as well.
The largest order of an element in this group is 45. For example, one such element of order 45 is (U R² L′).
元の位数とは、その操作を何回繰り返すと完全に元に戻るか。数えてみる。
位数は巡回の長さとひねりで決まる
角の置換を巡回に分けると、長さ k の巡回は k 回で角が元の場所に戻る。そのとき巡回に含まれる角のひねりの合計が 3 の倍数でなければ、角はひねられた状態で戻ってくるので、さらに 3 倍の 3k 回かかる。元の位数は各巡回の値の最小公倍数。
export function order(a: Cube): number {
const seen = [false, false, false, false, false, false, false, false];
let L = 1;
for (let i = 0; i < 8; i++) {
if (seen[i]) continue;
let j = i, len = 0, twist = 0;
while (!seen[j]) {
seen[j] = true;
twist += a.co[j];
j = a.cp[j];
len++;
}
L = lcm(L, twist % 3 ? 3 * len : len);
}
return L;
}
45 = lcm(15, 9) を作るには、ひねりの合計が 3 の倍数でない 5 巡回と 3 巡回が要る。角が 8 個必要になる。ところが位数 3,674,160 の群 H は DBL を固定しているので、動く角は 7 個しかない。
全元を歩く、紙の上で数える
2 通りで数えた。
- 全元を歩く: H の 3,674,160 元を番号から復元して位数を出す(2.8 秒)。G の 88,179,840 元は「H の元 × 持ち替え 24 通り」で全部作り、型付き配列で位数を出す(35.2 秒)
- 紙の上で数える: 角の巡回型(分割)ごとに、その型の置換の数 n!/∏k^{m_k} m_k! と、各巡回のひねりの合計が 0・1・2 になる割り当て(それぞれ 3^{k−1} 通り)を掛け、全体の合計が 3 の倍数になる組だけ足す。H は角 7 個、G は角 8 個
2 つの方法の表は完全に一致した。
| 位数 | H(3,674,160、DBL 固定) | G(88,179,840、全面回転) |
|---|---|---|
| 1 | 1 | 1 |
| 2 | 3,843 | 21,819 |
| 3 | 40,418 | 355,994 |
| 4 | 56,700 | 1,440,180 |
| 5 | 40,824 | 108,864 |
| 6 | 521,766 | 8,859,102 |
| 7 | 524,880 | 4,199,040 |
| 8 | · | 11,022,480 |
| 9 | 215,460 | 2,340,576 |
| 10 | 122,472 | 979,776 |
| 12 | 657,720 | 12,950,280 |
| 15 | 326,592 | 4,790,016 |
| 18 | 714,420 | 16,057,440 |
| 21 | · | 8,398,080 |
| 30 | 244,944 | 7,838,208 |
| 36 | 204,120 | 4,898,880 |
| 45 | · | 3,919,104 |
位数 3,674,160 の群の最大位数は 36(204,120 元)。最短の手順は 4 手で、U R′ F R2 など 24 通りある。45 は G にしかなく、3,919,104 元。例の U R² L′ も G の元で、L′ は DBL を含む面を回すので H には入らない(H は U・R・F で作る群)。
位数 8(8 巡回)が H に無いのも同じ理由で、7 個の角では作れない。位数 21(ひねった 7 巡回)も H には無い。7 巡回は動く角 7 個を全部含むので、ひねりの合計は必ず 3 の倍数になる。同じ理屈で G でも 8 巡回はひねれず、位数 24 はどちらにも無い。
いつ誰が書いたか
記事の版履歴を二分探索した。この文は 2025 年 2 月 8 日 21:13(UTC)に匿名の IP ユーザーが追加していて、最初の例は (URL′R) だった。同じ IP が 34 分後に (UR²L′) に書き換えている(L′ と R は向かいの面なので可換で、U R L′ R = U R² L′。同じ元)。追加した版から 2026 年 9 月 13 日の最新版までの 93 版で、この文はずっと残っている(抜き取りで確認)。日本語版にはこの記述はない。
それでも 45 は実物で起きる
では 45 は間違いなのかというと、キューブを手に持って U R² L′ を繰り返すと、45 回目で初めて揃って見える。持ち替えを許して「どの向きで見ても揃っていればよい」としても 45 回。ページの「repeat a sequence」の ×45 ボタンで試せる。
ここで区別が要る。
- 手順を k 回繰り返した状態は g^k(g は G の元)
- 「揃って見える」は g^k が持ち替え 24 通りのどれかになること。その最小の k を周期と呼ぶ。周期は位数の約数で、G の位数 45 の元 3,919,104 個は全部、周期も 45 だった
DBL を固定して持ち直すと、U R² L′ の 1 回分は U R になる(L′ は R′ と、x の持ち替えを一緒にしたもの)。U R の位数は 15。ところが 2 回目の U R² L′ は、持ち替えた後の向きで回すので、固定した DBL から見ると別の面を回している。1 回ごとに DBL 固定の手順に翻訳すると:
U R · F R · U F · R F · U F · R U · F U · R U · F R
9 回で持ち方が最初に戻り、この 18 手(H の元)の位数は 5。だから 9 × 5 = 45 回で揃う。
つまり「周期 45 の手順」はあるが、「位数 45 の元」は 3,674,160 の群にはない。局面を「持ち方を無視したもの」とみなすと群 H と同じ集合になるが、L・D・B を含む手順は、毎回同じ H の元として働くわけではない。Wikipedia の文は、G の性質(位数 45)と H の大きさ(3,674,160)を 1 つの「群」として書いてしまっている。正しくは「DBL を固定した群(位数 3,674,160)の最大位数は 36。全面回転の群(位数 88,179,840)では 45 で、例えば U R² L′。この手順は実物でも 45 回で初めて揃って見える」。
ページの実装
- 最短解は U・R・F だけで足りる。どの角が DBL の位置にあっても、U・R・F は向かい合う面の組から 1 つずつなので、持ち直した後の 9 手に対応する。Solve は「距離が 1 減る手」を表から選んでいくだけ
- ボタンのヒント: 18 手それぞれについて「押した後の距離」を表から引き、縮むなら緑、伸びるなら赤。D と U のように向かいの面は常に同じ値になる
- スクランブル: 距離ごとの局面数が分かっているので、「ちょうど 11 手」「ちょうど 14 手(90° 回転)」の局面を一様に引ける
- repeat a sequence: 手順を入力すると位数・周期・DBL を保つかを表示し、周期の回数だけ繰り返せる。x・y・z の持ち替えも書ける
- アニメーションの各手は、クリック時にまとめて
setTimeoutを予約している。タイマーを連鎖させるとヘッドレスのブラウザで途中で止まるため
公表値は手で写さない
tools/fetch-literature.mts が、英語版 Wikipedia の wikitext(版番号つき)、日本語版の wikitext、Jaap のページの HTML の表、OEIS の JSON、Bump & Auerbach の PDF(pdftotext)から数字を抜いて tools/literature.json に書く。tools/stats.mts がそれを読み、全部計算し直して項目ごとに突き合わせ、不一致は止めずに記録する。
丸めて印刷された値(平均や %)は、「こちらの正確な比を、その値が表示している桁数で丸めると一致するか」で判定している。
| 照合 | 値の数 | 一致 |
|---|---|---|
| 距離ごとの局面数(Wikipedia 英・日、OEIS) | 81 | 81 |
| 同時分布と合計(Jaap) | 207 | 207 |
| 局面数・神の数・平均 | 10 | 10 |
| 割合 a%・q%(Wikipedia) | 30 | 30 |
| 対称で割った局面数(Wikipedia) | 40 | 40 |
| R・U の群: 位数・直径・距離ごと(Bump & Auerbach、Wikipedia) | 21 | 21 |
| 対蹠点 18 個と、その間の距離 | 53 | 53 |
| 関係式 2 つ | 2 | 2 |
| 「最大位数 45」とその例 | 2 | 1 |
| 計 | 446 | 445 |
npm run stats は全部で 60.5 秒(ほとんどが G の全元の位数と対称類)。
まとめ
- 群を 2 つ区別する。 18 手で作れる G(88,179,840)と、DBL を固定した H(3,674,160)。G の元は H の元 × 持ち替えに一意に分解できるので、H が「持ち方を無視した局面」そのものになる
- 置換とひねりは独立に動く。 5,040 × 9 と 729 × 9 の表で、2 つの数え方の幅優先が 1 秒あまり。ブラウザで毎回作れる
- 鏡映はステッカーの置換で。 (cp, co) の算術ではなく 24 か所の置換にすると、48 対称が同じコードで扱える
- 積の向きを確かめる。 Bump & Auerbach の距離は、右から左に掛けたときだけ一致する
- 「群の元の位数」と「手順の周期」は別物。 3,674,160 の群の最大位数は 36。45 は 24 倍の群の値で、実物では U R² L′ が 45 回で揃って見えるが、その理由は 9 回で持ち方が戻ることと、その間の 18 手が位数 5 であること
数値はすべて npm run stats が出力する src/stats.json から、ページの notes とこの記事に写している。テスト 11 件。ソルバー内蔵パズル第 78 弾。
参考文献:
- Wikipedia, "Pocket Cube"(revision 1374673355, 2026年9月13日版。2026年9月29日参照)
- Wikipedia, 「ポケットキューブ」(2026年9月29日参照)
- Jaap Scherphuis, "Pocket Cube / Mini Cube", https://www.jaapsch.net/puzzles/cube2.htm (2026年9月29日参照)
- OEIS A079761, A079762
- D. Bump, D. Auerbach, "Unravelling the (miniature) Rubik's Cube through its Cayley Graph" (2006), Wayback Machine 経由
- H. Kociemba, "Cube Explorer" の CubieCube の定義(角の番号と回転)
デモ: https://sen.ltd/portfolio/pocket-cube/
リポジトリ: https://github.com/sen-ltd/pocket-cube
