在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。
概率质量公式为:
对于k = 0 , 1 , 2...n 其中
补充:
;
;
;
分析二项分析的递归算法:
if(N==0 && k==0) return 1.0; 和 if(N<0 || k<0) return 0.0; 表示的就是下面三个等式:
;
;
;
而 binomial(N, k, p)=(1.0-p)*binomial(N-1, k, p) + p*binomial(N-1, k-1, p) ,因为:
接下来使用循环数组替换递归算法:
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 ,对该网站表示感谢。