Skip to content

Commit 00254f6

Browse files
committed
修改文章
1 parent acdb84d commit 00254f6

2 files changed

Lines changed: 14 additions & 13 deletions

File tree

source/_posts/2023/09/从零开始的麻将AI论文复现(二).md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ disableNunjucks: false
4747
{% mahjong 111p %}
4848
→「3」
4949
{% mahjong 222333444s %}
50-
→「
50+
→「333
5151
{% mahjong 234456p %}
5252
→「11211」
5353

source/_posts/2026/03/使用打表法计算麻将向听数.md

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -60,17 +60,17 @@ cover:
6060

6161
我们主要来考虑一般型的向听数。根据向听数的定义,我们可以额外定义「0向听」表示听牌、「-1向听」为和牌。
6262

63-
### 常规的思考过程
63+
### 启发式的思考过程
6464

65-
那么如何计算向听数呢?首先我们来思考一下人类玩家自己是怎么计算向听数的。
65+
那么如何计算向听数呢?首先我们来思考一下人类玩家自己是怎么算的。不妨从和牌型倒着推算:
6666

6767
1. 一手已经组成四个面子和一个雀头的牌,是-1向听。
6868
2. 将其中一个面子换成一个搭子+一个孤张,则显而易见,这手牌的向听数+1。
6969
3. 将其中一个面子换成三个孤张,则向听数+2(其中两个孤张需要替换为第三个孤张的靠张才能形成面子)。
7070
4. 如果把雀头换为两张不同的牌,则在组合中没有其他对子的情况下,需要额外替换一张牌形成雀头,此时向听数需要再加1。
7171
5. 一手牌最多只需要4组面子+搭子的组合,因此对于搭子数而言,应设置其上限为4-面子数。
7272

73-
根据上面naive的思考,似乎一个完美的算法就这样形成了
73+
根据上面naive的思考,似乎一个完美的算法已经形成了
7474

7575
对于一手牌的某种拆分方法,记其中的面子数为 $m$,搭子数为 $d$,雀头数为 $q, q\in\left\lbrace0、1\right\rbrace$,可以计算出向听数 $s$ 如下:
7676

@@ -126,20 +126,19 @@ $m = 0,\ d=0,\ q=0,\ s=2\times 4-0-0=8$
126126
{% endnote %}
127127

128128

129-
好像基本没问题。容易发现,对于一手牌而言,其拆分方法可能有很多种,我们需要对它进行不同的拆分,得到上述参数后分别计算向听数,并取其最小值,才能得到最终的向听数。
129+
好像看起来没什么问题。容易发现,对于一手牌而言,其拆分方法可能有很多种,我们需要对它进行不同的拆分,得到上述参数后分别计算向听数,并取其最小值,才能得到最终的向听数。
130130

131131
### 特殊情形
132132

133133
偶然间发现这样一手牌:
134134

135135
{% mahjong 11112222333344z %}
136136

137-
138137
我们按上面向听数的计算公式,得到:
139138

140139
$$m=3,\ d=0,\ q=1, s=2\times 1-0-1=1$$
141140

142-
计算结果为1向听,但这一手牌移除3组搭子和一个雀头后,剩下的三个孤张「东南西」,没有一张能摸成搭子,故这手牌理应是2向听。
141+
计算结果为1向听,但这一手牌移除3组面子和一个雀头后,剩下的三个孤张「东南西」,没有一张能摸成搭子,故这手牌理应是2向听。
143142

144143
这种特殊情况的存在使得原先的算法需要进行一定程度的调整,经过一番搜索,我找到了这篇文章:
145144

@@ -156,7 +155,7 @@ $$m=3,\ d=0,\ q=1, s=2\times 1-0-1=1$$
156155

157156
为应对这些复杂的情况,文章作者提出了一些新的参数。
158157

159-
- G3:面子(顺子或刻子)的总数。-- group 3
158+
- G3:面子(顺子或刻子)的总数。
160159

161160
- G2:搭子(差一张牌就能变成面子)的总数(不能是脏搭子)。
162161

@@ -233,7 +232,7 @@ if P == 0:
233232

234233
当没有雀头时,我们至少需要额外一个进张来形成雀头,这会导致向听数+1。不过,根据剩余牌的类型,又能分为几类情况。
235234

236-
首先只有非脏牌的孤张牌才能形成雀头,这要求`R>0`,此时,我们可以直接用其中的某张孤牌来做雀头;否则,手上所有孤立牌都是爆满的,至少需要再进两张牌才行。这分别对应了上面代码中的 `s += 1`以及`s += 2`
235+
首先只有非脏牌的孤张牌才能形成雀头,这要求`R > 0`,此时,我们可以直接用其中的某张孤牌来做雀头;否则,手上所有孤立牌都处于爆满状态,已经摸不成雀头了,故至少需要再进两张牌才能凑一个雀头出来。这两种情况分别对应了上面代码中的 `s += 1`以及`s += 2`
237236

238237
既然进来了新的牌凑成了雀头,我们当然得打掉对应数量的牌,按牌的利用价值,我们优先打走脏字牌,其次是脏数牌。最后才会打走孤张非脏牌。
239238

@@ -252,17 +251,17 @@ if DZ > 0:
252251

253252
{% mahjong 11112222333344z %}
254253

255-
对于上面这手牌:G3=3, G2=0, DG2=0, P=1, DN=0, DZ=3。另外我们可以计算得到 K = 4, R = 0。
254+
对于上面这手牌,最优的参数组合是:G3 = 3, G2 = 0, DG2 = 0, P = 1, DN = 0, DZ = 3。另外我们可以计算得到 K = 4, R = 0。
256255

257-
`DZ -= G2 + 2 * R`这一步意味着我们想通过组面子的过程消耗掉一些脏字牌,在这个场景下,G2和R均为0,因此最后会剩余3张脏字牌。如果是三个普通的孤张,相比于一个面子而言,会“贡献”两向听,这一部分已经体现在 $s=2\times(4-m)-\max(d,4-m)-q$ 这个公式里了(每3张散牌的存在会减少一个面子),但如果三个孤张都是脏字牌——无法成搭,那么我们还需要额外的一次换牌,将其中一张脏字牌替换成普通的孤张,才能顺利组成面子,因此还需要额外+1。
256+
`DZ -= G2 + 2 * R`这一步意味着我们希望在组面子的过程中消耗掉一些脏字牌。上面这手牌中,G2和R均为0,因此最后会剩余3张脏字牌。如果是三个普通的孤张,相比于一个面子而言,会“贡献”两向听,这一部分已经体现在 $s=2\times(4-m)-\max(d,4-m)-q$ 这个公式里了(每3张散牌的存在会减少一个面子),但如果三个孤张都是脏字牌——无法成搭,那么我们还需要额外的一次换牌,将其中一张脏字牌替换成普通的孤张,才能顺利组成面子,因此还需要额外+1。
258257

259258
这也是最后这行代码`s += DZ // 3`所做的事。
260259

261260
---
262261

263262
在进行了以上调整后,函数计算向听数:`return 2 * (K - G3) - G2 + s`,这个公式本质上和前面那个naive的公式是一样的。
264263

265-
综上,这个函数对数量达到4张的牌做了更完善的处理
264+
综上,这个函数相当于对数量达到4张的牌做了更完善的处理
266265

267266
### 3n+1型的向听数计算
268267

@@ -347,6 +346,8 @@ $$
347346

348347
---
349348

350-
最后嫖了一个huggingface space,把小程序后端部署起来了。欢迎各位立直麻将爱好者使用~~测试bug~~
349+
最后嫖了一个huggingface space,把小程序后端部署起来了。本文相关代码可以在[这个space](https://huggingface.co/spaces/windshadow/riichi-toolkit/tree/main)中找到。
350+
351+
欢迎各位立直麻将爱好者使用~~帮我测试bug~~
351352

352353
![](https://blogfiles.oss.fyz666.xyz/png/0d565e1b-1e03-4a69-903d-76c2aed11ca0.PNG)

0 commit comments

Comments
 (0)