0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Grundy のゲーム(山を等しくなく 2 つに分ける)— Flammenkamp の「疎な値」1287 個を 2^26 まで計算し直して全部一致した

0
Posted at

Grundy のゲームをブラウザに実装した。1 手で山を 1 つ選び、大きさの違う 2 つの山に分ける。1 個と 2 個の山はもう分けられず、最後に分けた方が勝ち。山の大きさ n ごとに Grundy 値 G(n) があり、山が何個あっても値の XOR が 0 かどうかで勝敗が決まる。山を選ぶと切れる場所ごとに勝ち筋 ✓/負け筋 ✗ が出て、コンピュータと対戦できる。検算の題材は Achim Flammenkamp の公開ページで、そこには「疎な値」を持つ山 1287 個の全リスト、最大値が更新される山の一覧、山 0〜2^35 での値ごとの出現回数と最後の位置が載っている。値を「疎」と「普通」に分ける疎空間の方法で G(0)〜G(2^26 = 67,108,864) を Node.js で 1,017 秒かけて計算し、疎な山 1287 個は位置も値も全部一致、最大値の一覧は 75/75(OEIS A180120/A180121 は 76/76)、2^35 までの頻度表の疎な値 148 行も出現回数・最後の位置まで一致した。最後の 13 個の疎な山がすべて 4,746 万〜4,840 万に固まっている理由と、いちばん長い走査が「値 256 が初めて出る山」でちょうど止まることも計算から読めた。テスト 11 件。ソルバー内蔵パズル第 98 弾。

デモ: https://sen.ltd/portfolio/grundys-game/
リポジトリ: https://github.com/sen-ltd/grundys-game

Grundy のゲーム

ルール

  • 石の山がいくつかある。手番の人は山を 1 つ選び、大きさの違う 2 つの山に分ける。6 個の山なら 1 + 5 か 2 + 4 で、3 + 3 は禁止。
  • 1 個と 2 個の山は分けられない(2 個は 1 + 1 になってしまう)。
  • 分けられる山がなくなって手番が回ってきた方の負け。つまり最後に分けた方の勝ち。

P. M. Grundy が 1939 年に Eureka(ケンブリッジの学生誌)で取り上げたゲームで、Winning Ways の 4 章にも出てくる。石を取らずに分けるだけなので「石取り」の形をしていないが、Sprague–Grundy の理論がそのまま使える。

山を足し合わせる

  • 大きさ n の山の Grundy 値は G(n) = mex { G(a) ⊕ G(n − a) : 1 ≤ a < n − a }。分けた 2 山の値の XOR を全部の分け方について集め、そこに含まれない最小の非負整数(mex)を取る。
  • 山が何個あっても局面の値は各山の G の XOR。0 なら手番の人の負け、0 でなければ勝ちで、XOR を 0 にする分け方が必ずある。

G は 0, 0, 0, 1, 0, 2, 1, 0, 2, 1, 0, 2, 1, 3, 2, 1, 3, 2, 4, 3, 0, … と始まる(OEIS A002188)。デモは山を 1〜4 個置き、山を選ぶと石と石のすき間ごとに、そこで切ったあとの XOR が 0 なら ✓、そうでなければ ✗ を付ける。真ん中(等分)のすき間は点線で、クリックできない。コンピュータは ✓ の手があればそれを指し、なければいちばん大きい山から 1 個だけ切り離して粘る。

テストでは、値を使わない素朴な勝敗探索(山の多重集合をキーにしたメモ化)で石 24 個以下の全局面 7,338 通りを解き、XOR 規則と一致することを確かめた。

G(n) = 0 は 42 回しかない

負け局面になる単独の山(G(n) = 0)は 0, 1, 2, 4, 7, 10, 20, 23, 26, 50, 53, 270, … と続き、最後は 1222。OEIS A036685 の 42 項と一致し、2^26 まで計算してもそれ以降は 1 つも出なかった。G(n) が最終的に周期的になるかどうかは分かっていない(周期的なら 0 がもう出ないのは自然だが、証明はない)。

デモの下の図は n ≤ 30,000 の G(n) で、ページを開いたときに 30,000 項を約 0.3 秒で計算している。値はほぼ 3 刻みの帯に並んでゆっくり上がっていく。

疎空間:普通の値と疎な値

定義どおりに計算すると、山 n ごとに n/2 通りの分け方を見るので全体で O(N²) になる。手元の計測(tools/bench.mts)では N を倍にすると 4 倍かかり、N = 400,000 で 65.5 秒だった。この調子で 2^26 まで行くと、単純な外挿で 3 週間ほどかかる。

そこで Winning Ways と Flammenkamp の疎空間(sparse space)を使う。

  • 値 v の最下位ビット以外の 1 の個数が奇数なら「普通(common)」、偶数なら「疎(sparse)」と呼ぶ。マスクで書くと m = 0111…10 と v の AND のパリティ。
  • このとき 普通 ⊕ 普通 と 疎 ⊕ 疎 は疎、普通 ⊕ 疎 は普通になる(パリティは XOR で足し算になるから)。
  • 実際の G(n) はほとんどが普通の値で、疎な値を持つ山は 2^26 までに 1,287 個しかない。

これを使うと山 n の計算は 2 段になる。

  1. 普通の値の選択肢は、疎な山をちょうど 1 つ含む分け方からしか出ない。だから既知の疎な山 r(1,300 個弱)について G(r) ⊕ G(n − r) に印を付ければ、普通の値の選択肢は全部そろう。印の付かない最小の普通の値を c とする。
  2. c より小さい疎な値が選択肢に入っていないと、G(n) は c にならない。そこで分け方 a = 1, 2, … を順に見て値に印を付け、c 未満の疎な値が全部出そろったところで止める(G(n) = c)。分け方を見尽くしても出ない疎な値があれば、その最小のものが G(n) で、n は新しい疎な山になる。

2 段目の走査がどこで止まるかは何も仮定していないので、結果は定義どおりの値と同じになる。テストで 20,000 までの全項と、途中で区切って再開した場合を突き合わせている。速さは N を倍にすると 2 倍で、N = 400,000 が 6.8 秒、2^26 が 1,017 秒(Node.js 26、1 スレッド)。

Flammenkamp の表と突き合わせる

Flammenkamp のページ(Sprague-Grundy Values of Grundy's Game)には、2012 年に 2^38 余りまで計算した結果として、次の 3 つの表が載っている。HTML からそのまま抜き出して tools/data/flammenkamp.json に保存し、2^26 までの計算結果と比べた。

比べたもの 出典 一致
疎な値を持つ山と、その値 Flammenkamp の 1287 個のリスト 1287 / 1287(同じリスト)。最後は G(48,399,022) = 259
最大値が更新される山(2^26 まで) Flammenkamp の一覧 / OEIS A180120・A180121 75 / 75 と 76 / 76。最後は G(50,666,285) = 280
疎な値ごとの出現回数と最後の山 山 0〜2^35 の頻度表 148 / 148 行
G = 0 の山 OEIS A036685 同じ 42 個、最後は 1222
G(0)〜G(200,000) OEIS A002188 の a-file 200,001 / 200,001

頻度表は 2^35 までのものなので、普通の値の回数は 2^26 の計算とは比べられない。しかし疎な値は 4,840 万より先に 1 つも出てこないので、疎な値の行に限れば 2^26 までの数え上げとぴったり同じになるはずで、実際に 148 行すべて(出現 0 回の値も含む)で回数と最後の位置が一致した。逆に言うと、これで「2^26 から 2^35 の間に疎な山は出ない」という Flammenkamp の結果とも矛盾がない。

最大値の一覧のうち 2^26 を超える次の項は G(475,094,020) = 281 で、OEIS ではその後 Tomas Rokicki の計算で G(36,285,802,857,632) = 310 まで伸びている。

最後の 13 個はなぜ 4,700 万台に固まるのか

疎な山の分布は極端に偏っている。

範囲 疎な山の数
n ≤ 1,000 205
n ≤ 10,000 873
n ≤ 100,000 1,274(最後は 82,860)
100,000 < n ≤ 2^26 13(47,461,861 〜 48,399,022)

8 万から 4,746 万まで 1 つも出ず、そのあと 100 万の幅に 13 個が固まっている。しかもその 13 個の値は 258〜265 だけ。計算結果を見ると理由が読める。

  • 258〜265 は疎な値で、256 以上の値。2 つの値の XOR が 256 以上になるには、どちらかが 256 以上でなければならない(どちらも 256 未満なら XOR も 256 未満)。
  • 値が 256 以上の山は、この辺りでやっと現れ始める。G = 256 が初めて出るのは n = 7,250,049、それを超える値が初めて出るのは 47,339,607(257)、47,342,322(262)、47,342,972(263)。
  • 山の値(の mex 候補 c)が 258 を超えるようになると、2 段目の走査は「258 を作る分け方」を探さないといけない。作れる分け方は 256 以上の値の山を含むものに限られ、そういう山はまだ少ない。見つからなければ 258 がそのまま G(n) になり、疎な山が生まれる。

実際、計算全体でいちばん長い走査は n = 47,342,435 で起き、分け方 a = 1 から a = 7,250,049 まで見たところで止まった。7,250,049 は値 256 が初めて出る山そのもの。つまりこの山では、必要な疎な値を作れる分け方が「最初の 256 の山を切り出す」ものしかなく、そこに着くまで 725 万通りを見たことになる。256 以上の値の山が増えると疎な値は再び作れるようになり、4,840 万から先は 2^38 まで疎な山が出ない(Flammenkamp の計算)。

走査の長さ

2 段目の走査の長さ(普通の値の山について、何通りの分け方を見たか)は、

  • 平均 3,126.5。Flammenkamp のページには「平均して 3130 個の普通の値を確かめる必要があった」とあり、2^38 までの統計欄にも 3129.46 とある。ほぼ同じ量を測っているはず。
  • 最大 7,250,049(2^26 まで)。ページの統計欄の最大は 50,657,340 で、範囲が 4,000 倍以上広い。

平均はチェックポイントごとに見ても 3,119〜3,136 の間でほとんど動かず、計算量は N に比例する。

細かい食い違い

ページの履歴欄には「1992 年 1 月までに 80·10^6 まで計算し、それまで知られていた 1264 個に加えて 13 個の新しい疎な値を見つけた」とある。13 個は 100,000 より上の 13 個とぴったり合う。ただ、100,000 以下の疎な山は(n = 0 を含めて)1,274 個で、同じページのリスト自体も 1,274 + 13 = 1,287 になっている。1264 + 13 = 1277 はリストより 10 少なく、どこで数え方が違うのかはページからは分からなかった。

テスト

  • 疎空間の計算が定義どおりの O(n²) の計算と 20,000 まで一致し、途中で区切って再開しても同じ
  • OEIS A002188 の b-file(20,001 項)と一致
  • 100,000 までで、G = 0 の山が A036685、最大値の更新が A180120/A180121、疎な山が Flammenkamp のリストの先頭 1,274 個と一致
  • 出現回数の合計と最後の位置のつじつま
  • 値の 2 つのクラスが XOR で正しく閉じている(普通 ⊕ 普通 = 疎 など、0〜511 の全組)
  • 石 24 個以下の全局面で XOR 規則が値を使わない勝敗探索と一致し、勝ち手が XOR を 0 にする
  • 分け方の列挙(等分しない、1 と 2 は分けない)

実装メモ

  • src/grundy.ts: 定義どおりの計算 naiveValues、疎空間の掃引 Sweep(疎な山・最大値・頻度・最後の位置・走査の長さを記録)、対局用の moves / winningMove。
  • tools/compute.mts: 2^26 までの掃引。2^21 ごとに runs/compute.json を書き直すので、途中で止めてもそこまでの結果が残る。
  • tools/report.mts: Flammenkamp の表・OEIS との比較と、デモの右パネル(src/findings.json)の生成。記事の数字はここから取っている。
  • tools/bench.mts: 定義どおりの計算との速度比較。

まとめ

  • Grundy のゲームは、山が何個あっても Grundy 値の XOR で勝敗が決まる。デモでは切れる場所すべてに ✓/✗ を付けた。
  • 値を最下位ビット以外のパリティで「普通」と「疎」に分けると、普通の選択肢は疎な山 1,300 個弱を回るだけでそろい、残りは平均 3,100 通りほどの走査で済む。O(N²) が O(N) になり、2^26 まで 17 分。
  • Flammenkamp の疎な山 1287 個は位置も値も全部一致し、最大値の一覧、2^35 までの頻度表の疎な値、OEIS の 4 数列も一致した。
  • 最後の 13 個の疎な山が 4,746 万〜4,840 万に固まるのは、値 256 以上の山が現れたばかりで、258〜265 を作れる分け方がまだ少ないから。いちばん長い走査は、ちょうど最初の 256 の山で止まった。
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?