二項分佈算法(遞歸)

關於用遞歸實現的二項分佈算法

   最近在看Sedgewick的《算法》的時候有一題習題是關於改進用遞歸實現的二項分佈算法。這裏我令服從二項分佈爲$X\sim b(N,k,p)$,書本上習題給出的算法是:
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-p)*binomial(N-1,k,p)+p*binomial(N-1,k-1,p);
}

我們可能剛開始看這個遞歸算法的時候會不太明白(當時我都看懵逼了),那我們就從小一點的值來分析一下,假如N=2, k=1, p=0.5 ,即b(2, 1, 0.5) 。那麼我們按照這個算法來分析一下接下來的遞歸過程:二項分佈
我們可以看到每一次都可以分開兩部分然後再繼續遞歸,直到出現N=0 ,k=0 ,可能剛會不太明白爲什麼這個算法可以算出二項分佈,如果我們看一下我們的概率統計相關的書就可以知道有這樣一條遞歸公式:

(Nk)pk(1p)Nk=(1p)(N1k)pk(1p)N1k+p(N1k1)pk1(1p)N

你就會懂爲什麼有這個遞推算法了,而且上面的公式本質上其實是組合遞推公式:
(Nk)=(N1k)+(N1k1)

那麼我們搞懂了這個算法之後,我們會發現這個算法在N 很大的時候會發生遞歸層數很深,從我們上面這幅圖也可以看出來,每一個遞歸又會產生兩個遞歸,所以假設b(100, 50, 0.25) 的情況下函數將被遞歸調用大約在2.25210151.2671030 數量相當的龐大,反正我用這個算法運行的時候壓根就等不到運算結果。
所以後來就改了一下算法,改用大量循環,不用遞歸實現。
    //計算組合數
    public static double zuhe(double N, double k)
    {
        //模擬人類計算的約分過程從而減少階乘數量
        double m = N-k;
        double min = k;
        double max = m;
        double t = 0;

        double NN=1;
        double kk=1;
        if(min>max)
        {
            t=min;
            min = max;
            max=t;
        }
        //把大的階乘約分去掉
        while(N>max)
        {
            NN=NN*N;
            N--;
        }
        //計算小的階乘
        while(min>0)
        {
            kk=kk*min;
            min--;
        }
        //算出組合數
        return NN/kk;
    }
    //計算二項分佈值
    public static double binomial(int N,int k,double p)
    {
        double y=1;
        double s=1;
        //計算組合數
        double a =binomial(N,k);
        //計算(1-p)的(N-k)次方
        while((N-k)>0)
        {
            s=s*(1-p);
            N--;
        }
        //計算p的k次方
        while(k>0)
        {
            y=y*p;
            k--;
        }
        //最後三個值相乘得出二項分佈值
        return a*y*s;
    }
}

經過去掉遞歸後的算法就能很快的算出N 值比較大的情況下的二項分佈值。
如果你們還有更好的算法,可以告訴我,大家交流一下學習一下。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章