はじめに
本記事は小林 聡氏の記事「シャンテン数は如何に計算するべきか」に対する補足です。
同記事の結論である
目的に合わせて選択すればよい
には概ね同意します。一方で、tomohxx氏の手法に関する説明には、やや異なる理解に基づいて整理されている箇所が見受けられます。
特に以下の2点です。
- 「全和了形テーブル」が必要かどうか
- 役に対するシャンテン数をどのように扱うか
これらは実装と対応づけて整理することでより明確になります。本記事ではこの2点に限定して補足を行い、手法の優劣や最終的な選択には立ち入りません。
目次
シャンテン数の定義
前提と実装上の扱い
距離は定義であり実装ではない
tomohxx氏の手法
基本となる量(置換数)
単色への分解
部分置換数の事前計算
全体の合成
誤解されがちな点
「全和了形テーブルが必要」という誤解
補足:テーブルサイズについて
「役に対するシャンテン数計算が難しい」という誤解
まとめ
シャンテン数の定義
議論の前提として定義を整理します。
シャンテン数は「和了まであと何手か」を表す指標ですが、実装上はこれを和了形との距離として定式化できる点が重要です1。
ここでいう距離とは、ある手牌から和了形へ到達するために必要な牌の差分(不足している牌の枚数)です。ある手牌に対するシャンテン数は、すべての和了形のうち、この差分が最小となるものによって定まります。
この定義により、シャンテン数は手牌の枚数に依存せず、少牌・多牌を含む任意の手牌に対して一貫して定義できます。また、「役に対するシャンテン数」も同様に、入力の調整と通常のシャンテン数計算の組み合わせとして扱えます。
この問題は、手牌(牌の枚数分布)を頂点、1枚の牌を別の牌に置き換える操作(差分に対応する操作)を辺とするグラフ上の最短距離問題に帰着します。
前提と実装上の扱い
上記の定義は抽象的なものであり、具体的な計算では対象とするルールや実装に応じた前提が置かれます。
たとえば、一般的な麻雀ルールでは各牌は最大4枚まで存在するものとされますが、これはシャンテン数の定義そのものではなく、問題設定に由来する制約です。
また、副露の扱いについても、副露で使用されている牌は考慮せず、副露の数のみを考慮する形が一般的です(天鳳・雀魂など)。
たとえば、ポンした牌での単騎待ちは聴牌として扱います。
これは特定の手法に固有のものではなく、他のシャンテン数計算手法においても同様に見られる取り扱いです。
距離は定義であり実装ではない
ここでの距離は「何を求めるか」を規定する定義であり、「どう求めるか」は規定しません。
定義通りに実装すると
- 全和了形との距離計算
- 状態空間探索
が必要となり、計算量の観点から現実的ではありません。
重要なのは、定義と一致する値を返す限り実装は自由であるという点です。実用的なアルゴリズムは、この定義を満たしつつ計算量を削減したものと位置づけられます。
tomohxx氏の手法
tomohxx氏の手法は、上記の距離定義を満たしつつ実用的な計算を可能にした手法の一つです。
要点は次の3点です。
- 手牌の単色分解
- 単色ごとの事前計算
- 動的計画法による合成
基本となる量(置換数)
本手法ではシャンテン数の代わりに 置換数(シャンテン数 + 1) を用います。これは前述の距離に対応する量です。
単色への分解
手牌を数牌(萬子・筒子・索子)および字牌ごとに分解します。順子・刻子・雀頭はいずれも単一の種類の牌から構成されるため、各種類ごとに和了形への距離を独立に評価できます。
一方で、最終的な和了形として成立するためには、面子数や雀頭の取り方といった全体の構造制約を満たす必要があります。この制約は後段の合成で扱います。
このとき、各単色部分に対して定義される置換数を部分置換数と呼びます。
部分置換数の事前計算
単色(数牌1種または字牌)に限定すると、状態数は比較的小さくなります。そのため、すべての状態に対する部分置換数を事前計算しテーブル化できます。
ここでいう状態とは、各牌種に対する枚数(0〜4枚)の組、すなわち多重集合としての手牌を指します。
この計算は、定義通りに和了形との距離を求めてもよく、実際には枝刈りを伴う探索などによって効率的に構築することができます2。
重要なのは、この事前計算は実行時ではなく構築時に一度だけ行えばよい点です。テーブルをファイルとして保存すれば、利用時は読み込むだけで済みます。
テーブルは以下の対応を持ちます:
- 入力:単色の手牌(牌の枚数分布)
- 出力:和了形までの最小距離(部分置換数)
保持するのは距離のみであり、和了形そのものではありません。
単色の手牌状態は、各牌種について0〜4枚のいずれかを取るため、制約を設けずに(多牌を許容して)全状態を列挙すると状態数は以下の通りです。
- 数牌:5^9 = 1,953,125
- 字牌:5^7 = 78,125
さらに、実際に扱うのは「合計14枚以下」という制約を満たす状態に限られます。この制約を考慮するかどうかで状態数は次のように変わります。
| 種類 | 状態数(制約なし) | 状態数(14枚以下) |
|---|---|---|
| 数牌 | 1,953,125 | 405,350 |
| 字牌 | 78,125 | 43,130 |
したがって、必要なテーブルサイズは以下の通りです。
- 制約なし(多牌許容):1,953,125 + 78,125 = 2,031,250
- 14枚以下に制限:405,350 + 43,130 = 448,480
いずれの場合も現実的なサイズに収まります。
全体の合成
全体のシャンテン数(置換数)は、部分置換数を組み合わせ、面子数や雀頭の取り方といった全体の構造制約を満たすように最適化する問題として定式化されます。
これは動的計画法で効率的に処理できます。
誤解されがちな点
「全和了形テーブルが必要」という誤解
小林氏の記事では、tomohxx氏の手法に関して次のように述べられています。
また、和了形一覧という巨大な表を持たなければならないことはWebアプリの弱点になりえますし、
しかし、この指摘は実際の実装で用いられるデータ構造とは一致していません。ここで想定されている「巨大な表」とは、全和了形を列挙したテーブルです。
たとえば、面子分解や和了牌の区別を行わずに全和了形を列挙すると、組み合わせ数は以下の通りになります3。
| 和了形 | 組み合わせ数 |
|---|---|
| 0 面子 1 雀頭 | 34 |
| 1 面子 1 雀頭 | 1,836 |
| 2 面子 1 雀頭 | 49,332 |
| 3 面子 1 雀頭 | 875,418 |
| 4 面子 1 雀頭 | 11,498,658 |
| 合計 | 12,425,278 |
全和了形を列挙してテーブル化するとデータ量は膨大になり現実的ではありません。
しかし、tomohxx氏の手法はこのようなテーブルを使用しません。用いるのは単色ごとの部分置換数テーブルであり、和了形自体は保持しません。
したがって、この指摘は実装モデルの前提が異なっています。
補足:テーブルサイズについて
たとえば拙作の Rust 製ライブラリ xiangting4 では、数牌と字牌を合わせて
(405,350 + 43,130) × 4 byte = 1,793,920 byte(約1.8MB)
のメモリを使用します。
数MBのメモリを使用する点は利用環境によっては考慮が必要な要素となります。
「役に対するシャンテン数計算が難しい」という誤解
小林氏の記事では、tomohxx氏の手法に関して次のように述べられています。
そして tomohxx氏の手法では電脳麻将のAIの実装に必須である「役に対するシャンテン数」の計算が面倒です(全ての和了形を役に分類しなければならない)。
しかし、この点については実装上の扱いとはやや異なる整理になっています。
電脳麻将の手法においても、「その役を満たすすべての和了形に限定した距離」を厳密に計算しているわけではありません。実際には、
- 不要牌の削除
- 役牌対子の副露部分への移動
といった前処理を行った上で通常のシャンテン数計算を適用しています5。
したがって、「役に対するシャンテン数」を「その役を満たすすべての和了形」に限定した距離として直接計算することは、実装上の前提とはなっていません。この観点に立てば、tomohxx氏の手法においても同様に、入力の調整と通常の計算の組み合わせとして扱えます。
さらにtomohxx氏の手法では、必要な面子数 $m$ を外部から指定できます。これにより、目標とする和了形の構造を制御可能です。役に対するシャンテン数の計算においては、不要牌を削除する前の手牌枚数に基づいて必要な面子数を指定することで対応できます。
ここで重要なのは、この問題は特定の手法に固有のものではないという点です。役に対するシャンテン数を厳密な距離として定義する場合、すべての和了形を役で分類する必要が生じるため、いずれの手法においても計算は困難になります。一方で、実際の実装ではこのような厳密計算は行われず、入力の調整による近似が用いられています。
したがって、「役に対するシャンテン数計算が難しい」という性質は手法の違いによるものではなく、問題設定(厳密な距離を求めるかどうか)に依存するものです。この意味において、tomohxx氏の手法のみが特別に不利であるとは言えません。
まとめ
- シャンテン数は「和了形との距離」として定義できる
- 定義は実装方法を拘束しない
- tomohxx氏の手法は単色テーブルと動的計画法により実用化されている
- 全和了形テーブルは不要
- 役に対するシャンテン数も通常計算の拡張として扱える
最終的な選択は、
- 正確性(定義との一致)
- 実装・運用コスト(メモリ、初期化時間、配布サイズなど)
のトレードオフに依存します。
-
このような捉え方は特定の手法に固有のものではなく、従来から用いられている考え方の一つです。たとえば浅見了氏の記事では、加算法(有効牌を加えていく考え方)として説明されています。また同記事では「減算法」と呼ばれる方法も紹介されていますが、これは面子・搭子・対子といった構造からスコアを算出することでシャンテン数に対応する値を求める手法であり、本質的にはこの距離を効率的に計算するための評価方法とみなすことができます。 ↩
-
計算方法は[麻雀]天和の確率計算 #C++ - Qiitaを参照のこと ↩