二项分布(1.1.27 Binomial distribution)的递归算法,基于数组进行改进

    在Robert Sedgewick的《算法》(Algorithms)书中,有这么一道习题:

1.1.27 Binomial distribution. Estimate the number of recursive calls that would be used by the code

public static double binomial(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)*binomial(N-1, k, p) + p*binomial(N-1, k-1, p);
}

to compute binomial(100,50). Develop a better implementation that is based on saving computed values in an array.

中文意思是:

1.1.27 二项分布,估算使用 binomial(int N, int k, double p) 的代码计算binomial(100,50)的值,需要调用递归的次数。基于数组,保存计算的值,开发一个更好的实现。

    刚开始看这道题目的时候,真的是彻底蒙圈,搞不明白题目的递归算法为什么正确(大哭)。经过了好长时间的探索,终于明白了这个递归为什么正确了,现将探索的要点呈现在下面:

首先,要明白,二项分布的含义:

从N个独立的是/非实验中,成功次数的离散概率分布。每次实验的成功概率为p。

概率质量公式为: 

f(k;n;p)=\binom{n}{k}p^{k}(1-p)^{n-k}

对于k = 0 , 1 , 2...n   其中\binom{n}{k}=\frac{n!}{k!(n-k)!}

补充:

\binom{n}{k}=\binom{n-1}{k-1} + \binom{n-1}{k}

\binom{n}{n}=1

 \binom{n}{0}=1

  \binom{0}{k}=0

分析二项分析的递归算法:

if(N==0 && k==0) return 1.0; 和   if(N<0 || k<0)   return 0.0; 表示的就是下面三个等式:

\binom{n}{n}=1

 \binom{n}{0}=1

  \binom{0}{k}=0

而  binomial(N, k, p)=(1.0-p)*binomial(N-1, k, p) + p*binomial(N-1, k-1, p)  ,因为:

\binom{n}{k}p^{k}(1-p)^{(n-k)}=( \binom{n-1}{k-1}+\binom{n-1}{k})*p^{k}(1-p)^{(n-k)}

=\binom{n-1}{k-1}*p^{k}(1-p)^{(n-k)}+\binom{n-1}{k}*p^{k}(1-p)^{(n-k)}

=p*\binom{n-1}{k-1}*p^{k-1}(1-p)^{(n-k)}+(1-p)*\binom{n-1}{k}*p^{k}(1-p)^{(n-1-k)}

接下来使用循环数组替换递归算法:

public static double binomial02(int N, int k, double p){
    double[][] b =new double[N+1][k+1];
    for(int i=0;i<=N;i++){
        b[i][0] = Math.pow(1.0-p, i);
    }

    for(int i=1; i<=N; i++){
        for(int j=1; j<=k; j++ ){
            b[i][j] = p*b[i-1][j-1] + (1.0-p) * b[i-1][j];
        }
    }
    return b[N][k];
}

分析:

       我们为数组b分配了N+1 * k+1 个大小空间。

 b[0][0]  b[0][1]  b[0][2]  b[0][3]  b[0][4]  b[0][5]
 b[1][0]  b[1][1]  b[1][2]  b[1][3]  b[1][4]  b[1][5]
 b[2][0]  b[2][1]  b[2][2]  b[2][3]  b[2][4]  b[2][5]
 b[3][0]  b[3][1]  b[3][2]  b[3][3]  b[3][4]  b[3][5]
 b[4][0]  b[4][1]  b[4][2]  b[4][3]  b[4][4]  b[4][5]
 b[5][0]  b[5][1]  b[5][2]  b[5][3]  b[5][4]  b[5][5]
 b[6][0]  b[6][1]  b[6][2]  b[6][3]  b[6][4]  b[6][5]
 b[7][0]  b[7][1]  b[7][2]  b[7][3]  b[7][4]  b[7][5]
 b[8][0]  b[8][1]  b[8][2]  b[8][3]  b[8][4]  b[8][5]
 b[9][0]  b[9][1]  b[9][2]  b[9][3]  b[9][4]  b[9][5]
b[10][0] b[10][1] b[10][2] b[10][3] b[10][4] b[10][5]

该循环 for(int i=0;i<=N;i++)  b[i][0] = Math.pow(1.0-p, i);为第一列进行赋值。

双重循环并没有涉及对第一行和第一列的赋值。

我们可以看出b[10][5] =  0.25 * b[10 - 1][5 - 1] + 0.75 * b[10 -1 ][5]

而b[9][4] =  0.25 * b[8][3] + 0.75 * b[8 ][4]

b[9][5] =  0.25 * b[8][4] + 0.75 * b[8 ][5]

相当于  :

return (1.0 - p) * binomial(n - 1, k, p) + p * binomial(n - 1, k - 1, p);

也就是用循环数组的方式代替了递归调用。

说明:参考了 https://zhangjia.tv/670.html ,对该网站表示感谢。

猜你喜欢

转载自blog.csdn.net/hefrankeleyn/article/details/86345815
今日推荐