欢乐斗地主出牌方式统计
序
在集合S取出n个元素其和为K个数统计这篇文章中提到了笛卡尔积的这种解法,但是以笛卡尔积的方式求解的时候,我中午点击运行,我晚上回来的时候才输出了168条牌型组合,然后一直没有响应,后来粗略估算了一下,需要在1.44E+18条左右数据中进行筛选,而选出总手牌数为20条相当于大海捞针,所以需要重新对问题进行思考。
先将问题明确定义——“在N张手牌中,可能的出牌方式有多少种?”
牌型分析
以地主20张手牌为例,20张手牌可能1个炸弹也没有,最多有5个炸弹。所以20张手牌,炸弹的取值范围的取值范围是[0,5];
而3张相同的牌带1张单牌也是四张牌,所以20张手牌,出三张相同的牌带一张单牌的次数也是[0,5];
如果手牌中存在66667777这样的四张牌,6667和7776这种打法和一个6炸和7炸是两种打牌的方式。
同样就算是以6,6,6,6,7,7,7,7这样的8张单牌一次打出去,欺负下家或上家手里只有3,4,5了也不是不可以。
所以66667777这样8张牌在不考虑输赢的情况下也是有很多种打法的。而巧的是,我们讨论的是不考虑输赢的情况。
20张手牌有几种打出去的方式呢?
最多以20次单牌的形式打出去。
最多以10次对子的形式打出去。
最多以3次三连对的形式打出去。
依次类推,有了以下的表格:
具体实现
请下载C#版源代码,放到对应的控制台去执行即可。
这里列举几个示例结果:
手牌数为1
1 | 手牌数为1时,可能的打法情况为: |
手牌数为2
1 | 手牌数为2时,可能的打法情况为: |
手牌数为3
1 | 手牌数为3时,可能的打法情况为: |
手牌数为4
1 | 手牌数为4时,可能的打法情况为: |
根据以上结果中的示例2和示例5可知,就算是7778也可以以777和8分两次打出去。
手牌数为5
1 | 手牌数为5时,可能的打法情况为: |
限于篇幅,不再一一列举,根据程序列举出来的可能性如下图所示:
可知有17张手牌时,可以有1729种出牌方式。
可知有20张手牌时,可以有4815种出牌方式。
也许基于此,就有了赌徒说搏一搏,单车变摩托的说法。
注意,20张手牌的出牌方式并不能达到4815种,一个12张牌的顺子是12张牌,而再来两个炸弹8张牌,加起来也是20张牌。但是去掉3~A的12张牌,只能算出”2222”一个炸弹了。即”12张牌的顺子和2个炸弹”虽说符合代码的逻辑,但是并不符合现实中的牌型。换句话说,参考资料中的代码并没有考虑这种情况。
问题是还有哪些牌型组合在一起能满足总手牌数,但是不符合现实中的牌型呢?
总结
本以为在计算{1,2,3,4}中取出任意个数,与取出顺序无关时使用笛卡尔积的方式是很好的方式;结果在该问题中待处理数据竟达到了1.44E+18(1.44乘以10的18次方)条之多。
由此可知,问题并不是想当然就能够得到解决的,实践永远是检验真理的标准。
如果在每种牌型设置其对应的权重,所以在AI中的斗地主,如果能检测出手牌是10连对,或333444555666777带789JQ类型,得赶紧明牌不是么?