二项分布算法(伯努利实验)

二项分布

问题描述:

二项分布就是重复n次独立的伯努利试验。在每次试验中只有两种可能的结果,而且两种结果发生与否互相对立,并且相互独立,与其它各次试验结果无关,事件发生与否的概率在每一次独立试验中都保持不变,则这一系列试验总称为n重伯努利实验,当试验次数为1时,二项分布服从0-1分布。

假设伯努利实验中事件发生的概率为p,求n次独立的伯努利实验,事件发生k次的概率。


数学公式

我们都知道,上述问题可采用公式(1):

P(X=k)=Cknpk(1p)nk(1)

计算,其中
Ckn=n!k!(nk)!(2)

我们还知道:

Ckn=n!k!(nk)!=(n1)!k!(n1k)!+(n1)!(k1)!(nk)!=Ckn1+Ck1n1(3)

于是:

Cknpk(1p)nk=(1p)Ckn1pk(1p)n1k+pCk1n1pk1(1p)nk(4)

我们记

f(n,k)=Cknpk(1p)nk(5)

于是我们有:

f(n,k)=(1p)f(n1,k)+pf(n1,k1)(6)


算法:

递归算法:

算法思想:

递归算法思想就是来源于上面的公式(6),我们只需要找到递归出口,也就是最基本的情况就行了。
很明显:我们有

f(n,k)=(1p)f(n1,k)+pf(n1,k1)ifn>k,k>01.0 ifk=n=0(7)0.0 ifk>nn<0k<0

递归算法代码:
    /**
     * 时间复杂度:O(2^n)
     * @param N
     * @param K
     * @param p
     * @return 伯努利实验n次,事件发生k次的概率
     */
    public static double binomialRec(int N, int K, double p) {
        if (N == 0 && K == 0) 
            return 1.0;
        if (N < 0 || K < 0) 
            return 0.0;
        return (1.0 - p) * binomialRec(N - 1, K, p) + p * binomialRec(N - 1, K - 1, p);
    }

非递归算法

算法思想:

上述递归算法的时间复杂度太高,当n比较大时,可能都运行不出结果。之所以时间复杂度太高,是因为有大量的重复计算,于是很直观的想法就是,把已经算出来的结果存储到数组里。由于是计算实验n次,事件发生k次的概率,所以我们可以用二维数组存放计算的中间结果。

非递归算法代码:
/**
     * 
     * 伯努利实验,实验n次,出现k次的概率
     * 非递归方式实现,采用二维数组存储已经计算过的值
     * 时间复杂度:O(n * k),空间复杂度:O(n * k)
     * @param n
     * @param k
     * @param p
     * @return
     */
    public static double binomialArray(int n, int k, double p) {
        double[][] array = new double[n+1][k+1];
        // Initilize array
        for (int i = 0; i <= n; i++) {
            for (int j = 0; j <= k; j++) {
                array[i][j] = 0;
            }
        }
        array[0][0] = 1.0;
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j <= k; j++) {
                if (j > i) {
                    break;
                }
                if (i - 1 >= 0) {
                    array[i][j] += (1.0 - p) * array[i-1][j];
                    if (j - 1 >= 0) {
                        array[i][j] += p * array[i-1][j-1];
                    }
                }
            }
        }
        return array[n][k];
    }

非递归算法改进

算法思想: 上述非递归算法中,我们使用的是二维数组存放计算过的值,但我们发现,在计算array[i][j]时,只与其上一行的array[i-1][j]和array[i-1][j-1]有关,与其他值无关,所以我们可以使用一维数组来实现。这样空间复杂度可以降低到O(k).

非递归算法改进代码:
    /**
     * 伯努利实验,实验n次,出现k次的概率
     * 非递归方式实现,采用一维数组实现存储计算过的值
     * 时间复杂度:O(n * k),空间复杂度:O(k)
     * @param n
     * @param k
     * @param p
     * @return 
     */
    public static double binomial(int n, int k, double p) {
        double[] array = new double[k+1];
        for (int i = 0; i < array.length; i++) {
            array[i] = 0.0;
        }
        array[0] = 1.0;
        for (int i = 1; i <= n; i++) {
            // 这里要倒着计算,因为正序计算新值会覆盖掉之前的旧值
            for (int j = k; j >= 0; j--) {
                if (j - 1 >= 0) {
                    array[j] = (1.0 - p) * array[j] + p * array[j-1];
                }
                else {
                    array[j] = (1.0 - p) * array[j];
                }

            }
        }
        return array[k];

    }

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值