基于Monte Carlo方法近似概率:卡片结构定义与洗牌实现
卡片结构定义、洗牌实现与Monte Carlo概率模拟
1. 定义卡片结构与花色枚举
首先用枚举表示扑克牌的四种花色,再通过结构体组合花色与点数:
#include <stdio.h> #include <stdlib.h> #include <time.h> #include <stdbool.h> // 花色枚举 typedef enum { HEARTS, DIAMONDS, CLUBS, SPADES } Suit; // 卡片结构体 typedef struct { Suit suit; short rank; // 1=A, 2-10=数字, 11=J,12=Q,13=K } Card;
2. 随机洗牌函数
采用Fisher-Yates洗牌算法,这是无偏且高效的洗牌方式,能保证每张牌在每个位置的概率均等:
// 洗牌函数:随机打乱卡片数组 void shuffle(Card deck[], int deck_size) { // 初始化随机种子,仅需调用一次 static bool seed_initialized = false; if (!seed_initialized) { srand(time(NULL)); seed_initialized = true; } for (int i = deck_size - 1; i > 0; i--) { // 生成0到i的随机索引 int j = rand() % (i + 1); // 交换当前卡片与随机选中的卡片 Card temp = deck[i]; deck[i] = deck[j]; deck[j] = temp; } }
3. Monte Carlo 方法近似概率计算
以**“洗牌后第一张牌为红桃A”**的概率为例,通过多次重复洗牌实验,统计符合条件的次数,用频率近似概率:
实现步骤
- 初始化一副完整的扑克牌,标记红桃A的特征;
- 重复进行N次洗牌实验(N越大,近似精度越高);
- 每次洗牌后检查第一张牌是否为红桃A,统计命中次数;
- 最终概率 = 命中次数 / 总实验次数。
代码实现
// 创建一副完整的扑克牌 void create_deck(Card deck[]) { int index = 0; for (int suit = HEARTS; suit <= SPADES; suit++) { for (short rank = 1; rank <= 13; rank++) { deck[index].suit = (Suit)suit; deck[index].rank = rank; index++; } } } int main() { const int DECK_SIZE = 52; const int NUM_TRIALS = 1000000; // 100万次实验,保证精度 Card deck[DECK_SIZE]; int success_count = 0; create_deck(deck); for (int i = 0; i < NUM_TRIALS; i++) { Card temp_deck[DECK_SIZE]; // 复制原牌组避免每次重新创建 for (int j = 0; j < DECK_SIZE; j++) { temp_deck[j] = deck[j]; } shuffle(temp_deck, DECK_SIZE); // 检查第一张是否为红桃A(HEARTS,rank=1) if (temp_deck[0].suit == HEARTS && temp_deck[0].rank == 1) { success_count++; } } double probability = (double)success_count / NUM_TRIALS; printf("Monte Carlo近似概率:%.4f\n", probability); printf("理论概率:%.4f\n", 1.0 / 52); return 0; }
说明
- 实验次数建议设置为10万次以上,次数越多,结果越接近理论值(1/52 ≈ 0.0192);
- 可以修改统计条件,比如计算“洗牌后出现至少一对相同点数的牌”“同花顺”等复杂事件的概率,只需调整判断逻辑即可。
内容的提问来源于stack exchange,提问作者Mukund
相关产品推荐
相关产品推荐

