向聴数計算用に面子構成を取得する方法のメモだよ。

向聴数計算用に面子構成を取得する方法のメモだよ。

1.バックトラック法
ハッシュとか使わず再帰でぶん回して動的に面子構成調べる方法。
麻雀C言語プログラム集のとこに手頃なソースが公開されてる。
http://cmj3.web.fc2.com/#syanten
↑は「速度的にどうか疑問が残ります」とあるように高速化はされてない。
例えば1m1m1m1m2m3mから1m1m1m→1m2m3m→...と1m2m3m→1m1m1m→...の
重複した面子構成を取ってたりするので使うならどうにかする。
バックトラックとハッシュの比較はkmo2さんが昔書いてた。
http://kmo2.cocolog-nifty.com/prog/2009/06/post-d064.html

2.一色を10進数(?)でハッシュ化
あらさんの一人麻雀練習機にくっ付いてくるやつ。
1m2m3mなら000000111、1m2m2m3m3m3mなら000000123になる。
http://mahjong.ara3.net/etc/shanten/shanten8.htm
といっても、実は10進数ではないので10^9の範囲全てをキーに取るわけでなく、
一色の中で可能な牌の組み合わせパターンしかキーにしてない。
なのでサイズは405350→枝狩りして376728とかなり小さい。
他のハッシュ化方法に比べるとやや面倒だけど、地味に計算していけば
こいつもキー(牌姿)から牌の組み合わせパターンの何番目であるかを
シーケンスに求めることは可能(ただし枝狩り版は知らん)なので、
ハッシュを使わず、配列化して直接要素にアクセスする作りにもできる。

3.一色を5進数でハッシュ化
一種類の牌が最大4枚なので5進数で管理する方法。
1m2m3mなら1*1+1*5+1*25=31、1m2m2m3m3m3mなら1*1+2*5+3*25=86になる。
http://ara.moo.jp/mjhmr/shanten.htm
http://d.hatena.ne.jp/wistery_k/20130111/1357877008
割と簡単に作れてサイズも5^9=1953125=21bitと大きくないし、
ハッシュ値のまま加減算できる(1m2m2m3m3mに3mツモ→61+25)ので便利。
と思いきや、ハッシュ値から牌数に戻すときに剰余演算が必要なので
これで手牌周りを全部やろうとすると他の方法の定数倍遅くなる。
手牌は別の方法で管理して、ハッシュ化の一方通行のみで使うならあり。
もしくは剰余を軽くする神の一手を探して修羅の旅に出る。
http://d.hatena.ne.jp/wraith13/20081017/1224245469

4.一色を8進数でハッシュ化
一種類の牌が最大4枚→2進数で100の3bitなので8進数で管理する方法。
1m2m3mなら001001001→73、1m2m2m3m3m3mなら011010001→209になる。
3*9=27bitで一色を32bit内に収められる上にビット演算と加減算のみで
ハッシュ化も牌数に戻すのも高速に行える。オススメ。

5.バックトラックで取得済みの面子構成をハッシュ化
向聴数計算とは逸れるけど後の説明用に一応挙げておく。
面子種(刻子、順子、対子...)と色と数の組み合わせにユニークな
番号を振り、それを四面子一雀頭分につなげてハッシュに突っ込む。
多牌しながら和了までの数ツモ先までを読んでいく作りにしたときに、
牌姿中の面子構成をハッシュに突っ込んでおくと、多牌した牌姿全体は
一致しないけど同じ面子構成が含まれてるというときに有効になる。

6.全牌種を牌のつながり方でハッシュ化
項目5の「面子種」をもっと拡大解釈したイメージ。
隣の牌とつながる牌姿(1, 11,...4441, 4442の11530パターン)を
0で区切って最大14枚までの組み合わせで全牌種を表現する方法。
http://hp.vector.co.jp/authors/VA046927/mjscore/mjalgorism.html
全牌種が計2197084パターンの22bitに収まる。
・・・はずなんだけど、↑の発案者は81bit必要、と言って謎の符号化を
適用して27bitに圧縮している部分がよく分かってない。
101010101010101010101010101(27桁)〜の時点で14枚あるわけだし、
同じ牌の個数が4枚〜は4040402等に回収されるんじゃないんだろか?
一応、コード書いてみて符号化の必要はなさそうな感じはするけど、謎。
https://github.com/t-takasaka/mahjong-hash-all-hand/blob/master/main.cpp
まあアヤシイ部分はあるにせよ、向聴数計算全般に使えそう。
あと、牌同士の相対位置しか持ってないので、役判定はもう一度手牌と
照らし合わせる必要がある部分も、どうにかならないかというところ。


ハッシュ化という言葉の使い方を間違えている気がしないでもない!
(最小完全ハッシュ関数を探してるのでハッシュ化という言い方でもいいの?)