你的斗地主能拿多少炸?


声明:本文转载自https://my.oschina.net/u/3491123/blog/1621647,转载目的在于传递更多信息,仅供学习交流之用。如有侵权行为,请联系我,我会及时删除。

最近无聊,想知道一下玩斗地主的话我能有多大的概率拿到炸弹(4张同点数牌 或 集齐大小王)。但是我概率学学得不好,于是想到用统计学来试试,随手写了一个程序模拟一下斗地主的发牌过程

面向对象Card

首先依据OOP思想,我把牌看作是一个对象,点数与花色是其属性,为了处理大小王加入了Type属性

public class Card {     Suit suit;     Size size;     Type type;      Card(Suit suit, Size size) {         this.suit = suit;         this.size = size;         this.type = Type.Ordinary;     }      Card(Type type) {         if (type.equals(Type.Ordinary)) {             throw new RuntimeException("非法参数");         }         this.type = type;     } } 

三个属性我都用了枚举类来表示,纯粹是因为既然是面向对象,那么就纯粹一点

public enum Size {     P3(0), P4(1), P5(2), P6(3), P7(4), P8(5), P9(6),     P10(7), J(8), Q(9), K(10), A(11), P2(12);      int sequence;      Size(int sequence) {         this.sequence =  sequence;     } } 

Size表示点数的大小,按从小到大排序。加入sequence属性目的是为了在统计时方便处理

public enum Suit {     Spade(4), Heart(3), Club(2), Diamond(1);      // 权重     int weight;      Suit(int weight) {         this.weight = weight;     } } 

Suit花色,加入了weight属性作为大小权重,斗地主花色不分大小,不过有些牌会区分,所以随手加一下

public enum Type {     Ordinary(0), LITTLE_JOKER(1), BIG_JOKER(2);      int weight;      Type(int weight) {         this.weight = weight;     } } 

Type牌类型,主要是为了特殊处理大小王,根据权重值,小王比大王小~

计算过程

首先抽象一下玩牌的几个步骤,第一步:拿出一副牌;第二步:洗牌(随机打乱);第三步:发牌;第四步:Play(计算是否有炸弹);我简化了发牌方法,放进主程序中,其他步骤具体实现如下

/**      * 生成一套有序牌数组      */     static Card[] newCards() {         // 牌组用数组表示         Card[] cards = new Card[54];         // 游标i         int i = 0;         // 循环放牌 13种大小 * 4种花色 = 52         for (Size point : Size.values()) {             for (Suit suit : Suit.values()) {                 cards[i++] = new Card(suit, point);             }         }         // 插入大小王         cards[52] = new Card(Type.LITTLE_JOKER);         cards[53] = new Card(Type.BIG_JOKER);         return cards;     }      /**      * 洗牌      * @param cards 打乱的牌组      */     static Card[] shuffle(Card[] cards) {         Random random = new Random();         int len = cards.length;         // 复杂的 O(n)         // 遍历一副牌,每次循环随机取一张当前牌后面的一张牌(含当前牌)与当前牌交换         // 在完全随机的情况下,每张牌在每个位置的概率应该一致,共有 n! 种情况         for (int i = 0;i < len; i++) {             int r = random.nextInt(len - i);             change(i, r + i, cards);         }         return cards;     }      // 简单的交互位置方法     static void change(int a, int b, Card[] cards) {         Card temp = cards[a];         cards[a] = cards[b];         cards[b] = temp;     }          static final int DOUBLE_JOKER = 3;     static final int FULL_SUIT = 10;     /**      * 判断是否有炸弹      * @param cards 牌组      * @return true 有炸弹 false 无炸弹      */     static boolean hasBoom(Card[] cards) {         // 构造一个与Size数量等长的初始为0的数组         int[] counter = new int[Size.values().length]; //初始化为0         // 特殊处理大小王         int weightOfJoker = 0;         for (Card card: cards) {             // 特殊处理大小王             if (!card.type.equals(Type.Ordinary)) {                 weightOfJoker += card.type.weight;                 // 大小王权重和为3时即王炸                 if (weightOfJoker == DOUBLE_JOKER) {                     return true;                 }                 continue;             }             // 利用点数序列值为下标,加上权重值,和为10时即凑足4张牌             counter[card.size.sequence] += card.suit.weight;             if (counter[card.size.sequence] == FULL_SUIT) {                 return true;             }         }         return false;     } 

游戏主方法,来算算地主和农民各有多少概率吧

public static void main(String[] args) {         long gameStart = System.currentTimeMillis();         int gameTime = 100000;         // 农民17张牌计数器         int nongHasBoom = 0;         // 地主20张牌计数器         int diHasBoom = 0;         // 运行游戏         for (int i = 0;i < gameTime; i++) {             // 拿到一副新牌             Card[] poker = newCards();             // 洗牌             poker = shuffle(poker);             // 发牌 在随机的情况下,连续发和分开发理论上不影响你拿牌的概率,简化             Card[] nong = Arrays.copyOf(poker, 17);             Card[] di = Arrays.copyOfRange(poker, 17, 17 + 20);             nongHasBoom += hasBoom(nong)? 1 : 0;             diHasBoom += hasBoom(di)? 1 : 0;         }         long gameEnd = System.currentTimeMillis();         System.out.println(String.format("地主炸弹概率 %f , 农民炸弹概率 %f", diHasBoom * 1.0 / gameTime, nongHasBoom * 0.1 / gameTime));         System.out.println(String.format("运行时 %f s", (gameEnd - gameStart)/1000.0));     } 

运行一次程序,发现运行速度还挺快,反正10万次不足半秒,运行才发现,地主三张牌对拿炸弹的概率影响竟然这么大,可以提升将近一倍的概率。当然程序是我随便写的,可能存在不严谨导致数据错误的地方,如果发现还请斧正。其次在枚举类的书写规范上,我偷了一些懒,没有全部大写~

地主炸弹概率 0.302310 , 农民炸弹概率 0.186460 运行时 0.217000 s 

本文发表于2018年02月13日 08:31
(c)注:本文转载自https://my.oschina.net/u/3491123/blog/1621647,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如有侵权行为,请联系我们,我们会及时删除.

阅读 1618 讨论 0 喜欢 0

抢先体验

扫码体验
趣味小程序
文字表情生成器

闪念胶囊

你要过得好哇,这样我才能恨你啊,你要是过得不好,我都不知道该恨你还是拥抱你啊。

直抵黄龙府,与诸君痛饮尔。

那时陪伴我的人啊,你们如今在何方。

不出意外的话,我们再也不会见了,祝你前程似锦。

这世界真好,吃野东西也要留出这条命来看看

快捷链接
网站地图
提交友链
Copyright © 2016 - 2021 Cion.
All Rights Reserved.
京ICP备2021004668号-1