binomial_distribution.js

import epsilon from "./epsilon.js";

/**
 * [二项分布](http://en.wikipedia.org/wiki/Binomial_distribution) 是离散概率分布,
 * 表示在 n 次相互独立的成功/失败实验中,成功的次数。这类成功/失败实验也称为
 * 伯努利试验(Bernoulli trial);当试验次数 `trials = 1` 时,二项分布退化为
 * 伯努利分布(Bernoulli Distribution)。
 *
 * @param {number} trials 试验次数
 * @param {number} probability 成功的概率
 * @returns {number[]} 输出概率分布
 */
function binomialDistribution(trials, probability) /*: ?number[] */ {
    // 检查 `probability` 是否为有效概率(0 ≤ p ≤ 1),
    // 并确保 `trials` 是正整数。
    if (probability < 0 || probability > 1 || trials <= 0 || trials % 1 !== 0) {
        return undefined;
    }

    // 初始化 `x`(随机变量)和 `cumulativeProbability`(累积分布函数)。
    // `cells` 用于存储概率质量函数(PMF)值。
    // `binomialCoefficient` 用于计算二项系数。
    let x = 0;
    let cumulativeProbability = 0;
    const cells = [];
    let binomialCoefficient = 1;

    // 该算法遍历所有可能的结果,直到 `cumulativeProbability` 接近 1,
    // 此时我们已经覆盖了该分布的绝大部分概率质量。
    do {
        // 计算 [概率质量函数(PMF)](https://en.wikipedia.org/wiki/Probability_mass_function)
        cells[x] =
            binomialCoefficient *
            Math.pow(probability, x) *
            Math.pow(1 - probability, trials - x);
        cumulativeProbability += cells[x];
        x++;
        binomialCoefficient = (binomialCoefficient * (trials - x + 1)) / x;
        // 当 `cumulativeProbability` 接近 1 时,说明我们已经计算了
        // 该分布的主要概率范围。
    } while (cumulativeProbability < 1 - epsilon);

    return cells;
}

export default binomialDistribution;