在 C 语言中使用蒙特卡洛模拟进行概率计算

CBeginner
立即练习

简介

在本实验中,我们将探索如何使用蒙特卡洛模拟(Monte Carlo simulation)在 C 编程中估计概率。我们将从定义一个简单的随机实验开始,比如抛硬币,然后运行多次试验来统计成功结果的数量。最后,我们将通过成功次数除以试验总次数来计算估计概率。本实验通过 C 语言对概率和组合数学的基本概念进行了实际介绍,这些都是数据分析和决策的必备技能。

定义一个随机实验

在这一步中,我们将探索如何在 C 语言中使用蒙特卡洛模拟来定义一个随机实验。随机实验是一个结果不确定的过程,可以使用概率技术进行模拟。

理解随机实验

让我们创建一个简单的 C 程序来演示一个基本的随机实验:抛硬币模拟。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define NUM_TRIALS 1000

int main() {
    // 为随机数生成器设定种子
    srand(time(NULL));

    // 正面朝上的次数
    int heads_count = 0;

    // 模拟抛硬币
    for (int i = 0; i < NUM_TRIALS; i++) {
        // 生成 0 或 1 的随机数
        int flip = rand() % 2;

        // 统计正面朝上的次数
        if (flip == 0) {
            heads_count++;
        }
    }

    // 计算正面朝上的概率
    double probability = (double)heads_count / NUM_TRIALS;

    printf("抛硬币实验:\n");
    printf("总试验次数:%d\n", NUM_TRIALS);
    printf("正面朝上的次数:%d\n", heads_count);
    printf("正面朝上的估计概率:%.2f\n", probability);

    return 0;
}

示例输出:

抛硬币实验:
总试验次数:1000
正面朝上的次数:502
正面朝上的估计概率:0.50

关键概念解释

  1. 随机数生成
    • srand(time(NULL)) 为随机数生成器设定种子
    • rand() % 2 以相等的概率生成 0 或 1
  2. 实验设计
    • 我们将抛硬币定义为我们的随机实验
    • 运行多次试验(在这种情况下为 1000 次)
    • 统计成功结果(正面朝上)的数量
  3. 概率估计
    • 概率 = (成功结果的数量)/(试验的总次数)
    • 在这种情况下,我们预计正面朝上的概率接近 0.5

编译并运行程序

## 创建源文件
nano ~/project/coin_flip_experiment.c

## 编译程序
gcc ~/project/coin_flip_experiment.c -o ~/project/coin_flip_experiment

## 运行实验
~/project/coin_flip_experiment

示例编译和运行输出:

## 编译
gcc ~/project/coin_flip_experiment.c -o ~/project/coin_flip_experiment

## 执行
~/project/coin_flip_experiment
抛硬币实验:
总试验次数:1000
正面朝上的次数:502
正面朝上的估计概率:0.50

运行多次随机试验并统计成功次数

在这一步中,我们将通过运行多次随机试验并准确统计成功结果来扩展我们的蒙特卡洛模拟。我们将通过一个更复杂的概率实验来演示这一点:使用随机点生成来估计π(圆周率)。

使用蒙特卡洛方法估计π

我们将使用一种几何方法,通过在包含四分之一圆的正方形内生成随机点来估计π。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>

#define NUM_TRIALS 100000

int main() {
    // 为随机数生成器设定种子
    srand(time(NULL));

    // 总点数和圆内点数的计数器
    int total_points = 0;
    int points_inside_circle = 0;

    // 运行多次试验
    for (int i = 0; i < NUM_TRIALS; i++) {
        // 生成 0 到 1 之间的随机 x 和 y 坐标
        double x = (double)rand() / RAND_MAX;
        double y = (double)rand() / RAND_MAX;

        // 检查点是否在四分之一圆内
        if (sqrt(x*x + y*y) <= 1.0) {
            points_inside_circle++;
        }
        total_points++;
    }

    // 估计π
    double pi_estimate = 4.0 * points_inside_circle / total_points;

    printf("π估计实验:\n");
    printf("总点数:%d\n", total_points);
    printf("圆内点数:%d\n", points_inside_circle);
    printf("估计的π:%.6f\n", pi_estimate);
    printf("实际的π:    %.6f\n", M_PI);

    return 0;
}

编译并运行实验

## 创建源文件
nano ~/project/pi_estimation.c

## 编译程序(注意链接数学库)
gcc ~/project/pi_estimation.c -o ~/project/pi_estimation -lm

## 运行实验
~/project/pi_estimation

示例输出:

π估计实验:
总点数:100000
圆内点数:78540
估计的π:3.141600
实际的π:    3.141593

关键概念解释

  1. 多次试验
    • 我们运行大量的随机试验(100,000 次)
    • 每次试验在一个 1x1 的正方形内生成一个随机点
  2. 统计成功次数
    • 跟踪总点数和四分之一圆内的点数
    • 成功定义为点落在圆内
  3. 概率估计
    • 概率 = (成功点数)/(总点数)
    • 由于使用了四分之一圆的方法,乘以 4 来估计π

重要技术

  • (double)rand() / RAND_MAX 生成 0 到 1 之间的随机小数
  • sqrt(x*x + y*y) 计算到原点的距离
  • 大量试验可提高估计精度

估计概率 = 成功次数 / 试验次数

在这最后一步中,我们将通过在一个更实际的场景中分析成功结果与总试验次数的比率,来演示如何计算概率。

掷骰子概率实验

我们将模拟掷两个骰子,并计算得到特定总和(例如,总和为 7)的概率。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define NUM_TRIALS 100000
#define TARGET_SUM 7

int roll_die() {
    return rand() % 6 + 1;
}

int main() {
    // 为随机数生成器设定种子
    srand(time(NULL));

    // 总掷骰子次数和成功掷骰子次数的计数器
    int total_rolls = 0;
    int successful_rolls = 0;

    // 运行多次试验
    for (int i = 0; i < NUM_TRIALS; i++) {
        // 掷两个骰子
        int die1 = roll_die();
        int die2 = roll_die();

        // 检查总和是否与目标匹配
        if (die1 + die2 == TARGET_SUM) {
            successful_rolls++;
        }
        total_rolls++;
    }

    // 计算概率
    double probability = (double)successful_rolls / total_rolls;

    printf("掷骰子概率实验:\n");
    printf("目标总和:%d\n", TARGET_SUM);
    printf("总掷骰子次数:%d\n", total_rolls);
    printf("成功掷骰子次数:%d\n", successful_rolls);
    printf("估计概率:%.4f\n", probability);

    // 用于比较的理论概率
    printf("理论概率:%.4f\n", 1.0/6);

    return 0;
}

编译并运行实验

## 创建源文件
nano ~/project/dice_probability.c

## 编译程序
gcc ~/project/dice_probability.c -o ~/project/dice_probability

## 运行实验
~/project/dice_probability

示例输出:

掷骰子概率实验:
目标总和:7
总掷骰子次数:100000
成功掷骰子次数:16644
估计概率:0.1664
理论概率:0.1667

关键概念解释

  1. 概率计算
    • 概率 = (成功结果的数量)/(试验的总次数)
    • 在这种情况下:成功掷骰子次数 / 总掷骰子次数
  2. 蒙特卡洛模拟
    • 大量试验(100,000 次)提供了准确的估计
    • 模拟概率与理论概率非常接近
  3. 随机性和准确性
    • srand(time(NULL)) 确保不同的随机序列
    • 更多的试验提高估计的准确性

概率解释

  • 估计概率(0.1664)非常接近理论概率(1/6 ≈ 0.1667)
  • 展示了蒙特卡洛方法如何估计概率

总结

在本实验中,我们学习了如何在 C 语言中使用蒙特卡洛模拟来定义一个随机实验。我们创建了一个简单的抛硬币模拟来演示关键概念。首先,我们为随机数生成器设定种子并模拟抛硬币,统计成功结果(正面朝上)的数量。然后,我们通过将成功结果的数量除以试验的总次数来估计正面朝上的概率。输出结果显示,对于公平的抛硬币,估计概率接近预期值 0.5。