// NewBinomial returns a Binomial distribution. func NewBinomial(n int, p float64, src rand.Source) *Binomial { if float64(n)*math.Min(p, 1.0-p) <= 0 { panic("Illegal argument for Binomial distribution: n*p <= 0") } binomial := &Binomial{N: n, P: p} binomial.randomGenerator = rand.New(src) binomial.n_last = -1 binomial.n_prev = -1 binomial.p_last = -1.0 binomial.p_prev = -1.0 binomial.log_p = math.Log(binomial.P) binomial.log_q = math.Log(1.0 - binomial.P) binomial.log_n = specfunc.LogFactorial(binomial.N) return binomial }
// Pdf returns the probability distribution function. func (poisson Poisson) Pdf(k int) (p float64) { p = math.Exp(float64(k)*math.Log(poisson.Mean) - specfunc.LogFactorial(k) - poisson.Mean) return }
// Helper function func (poisson Poisson) f(k int, l_nu, c_pm float64) float64 { return math.Exp(float64(k)*l_nu - specfunc.LogFactorial(k) - c_pm) }
// Generate poisson random number // Implementation: High performance implementation. // Patchwork Rejection/Inversion method. // This is a port of Poisson.java from the colt java library, which is based upon // H. Zechner (1994): Efficient sampling from continuous and discrete unimodal distributions, // Doctoral Dissertation, 156 pp., Technical University Graz, Austria. // Also see // Stadlober E., H. Zechner (1999), The patchwork rejection method for sampling from unimodal distributions, // to appear in ACM Transactions on Modelling and Simulation. func (poisson *Poisson) random() (k int) { /****************************************************************** * * * Poisson Distribution - Patchwork Rejection/Inversion * * * ****************************************************************** * * * For parameter my < 10 Tabulated Inversion is applied. * * For my >= 10 Patchwork Rejection is employed: * * The area below the histogram function f(x) is rearranged in * * its body by certain point reflections. Within a large center * * interval variates are sampled efficiently by rejection from * * uniform hats. Rectangular immediate acceptance regions speed * * up the generation. The remaining tails are covered by * * exponential functions. * * * *****************************************************************/ my := poisson.Mean if my < SWITCH_MEAN { // CASE B: Inversion- start new table and calculate p0 if my != poisson.my_old { poisson.my_old = my poisson.llll = 0 poisson.p = math.Exp(-my) poisson.q = poisson.p poisson.p0 = poisson.p } if my > 1.0 { poisson.m = int(my) } else { poisson.m = 1 } for { u := poisson.randGenerator.Float64() if u <= poisson.p0 { return k } if poisson.llll != 0 { if u > 0.458 { if poisson.llll < poisson.m { k = poisson.llll } else { k = poisson.m } } else { k = 1 } for ; k <= poisson.llll; k++ { if u <= poisson.pp[k] { return k } } if poisson.llll == 35 { continue } } for k = poisson.llll + 1; k <= 35; k++ { poisson.p *= my / float64(k) poisson.q += poisson.p poisson.pp[k] = poisson.q if u <= poisson.q { poisson.llll = k return k } } poisson.llll = 35 } } else { var ( Dk, X, Y int Ds, U, V, W float64 ) poisson.m = int(my) if my != poisson.my_last { poisson.my_last = my // approximate deviation of reflection points k2, k4 from my - 1/2 Ds = math.Sqrt(my + 0.25) // mode m, reflection points k2 and k4, and points k1 and k5, which // delimit the centre region of h(x) poisson.k2 = int(math.Ceil(my - 0.5 - Ds)) poisson.k4 = int(my - 0.5 + Ds) poisson.k1 = int(poisson.k2 + poisson.k2 - poisson.m + 1.0) poisson.k5 = int(poisson.k4 + poisson.k4 - poisson.m) // range width of the critical left and right centre region poisson.dl = float64(poisson.k2 - poisson.k1) poisson.dr = float64(poisson.k5 - poisson.k4) // recurrence constants r(k) = p(k)/p(k-1) at k = k1, k2, k4+1, k5+1 poisson.r1 = my / float64(poisson.k1) poisson.r2 = my / float64(poisson.k2) poisson.r4 = my / float64(poisson.k4+1) poisson.r5 = my / float64(poisson.k5+1) // reciprocal values of the scale parameters of expon. tail envelopes poisson.ll = math.Log(poisson.r1) // expon. tail left poisson.lr = -math.Log(poisson.r5) // expon. tail right // Poisson constants, necessary for computing function values f(k) poisson.l_my = math.Log(my) poisson.c_pm = float64(poisson.m)*poisson.l_my - specfunc.LogFactorial(poisson.m) // function values f(k) = p(k)/p(m) at k = k2, k4, k1, k5 poisson.f2 = poisson.f(poisson.k2, poisson.l_my, poisson.c_pm) poisson.f4 = poisson.f(poisson.k4, poisson.l_my, poisson.c_pm) poisson.f1 = poisson.f(poisson.k1, poisson.l_my, poisson.c_pm) poisson.f5 = poisson.f(poisson.k5, poisson.l_my, poisson.c_pm) // area of the two centre and the two exponential tail regions // area of the two immediate acceptance regions between k2, k4 poisson.p1 = poisson.f2 * (poisson.dl + 1.0) // immed. left poisson.p2 = poisson.f2*poisson.dl + poisson.p1 // centre left poisson.p3 = poisson.f4*(poisson.dr+1.0) + poisson.p2 // immed. right poisson.p4 = poisson.f4*poisson.dr + poisson.p3 // centre right poisson.p5 = poisson.f1/poisson.ll + poisson.p4 // expon. tail left poisson.p6 = poisson.f5/poisson.lr + poisson.p5 // expon. tail right } // end set-up for { // generate uniform number U -- U(0, p6) // case distinction corresponding to U U = poisson.randGenerator.Float64() * poisson.p6 if U < poisson.p2 { // immediate acceptance region R2 = [k2, m) *[0, f2), X = k2, ... m -1 V = U - poisson.p1 if V < 0.0 { k = poisson.k2 + int(U/poisson.f2) return } // immediate acceptance region R1 = [k1, k2)*[0, f1), X = k1, ... k2-1 W = V / poisson.dl if W < poisson.f1 { k = poisson.k1 + int(V/poisson.f1) return } // computation of candidate X < k2, and its counterpart Y > k2 // either squeeze-acceptance of X or acceptance-rejection of Y Dk = int(poisson.dl*poisson.randGenerator.Float64()) + 1 if W <= poisson.f2-float64(Dk)*(poisson.f2-poisson.f2/poisson.r2) { k = poisson.k2 - Dk return } V = poisson.f2 + poisson.f2 - W if V < 1.0 { Y = poisson.k2 + Dk if V < poisson.f2+float64(Dk)*(1.0-poisson.f2)/(poisson.dl+1.0) { k = Y return } if V <= poisson.f(Y, poisson.l_my, poisson.c_pm) { k = Y return } } X = poisson.k2 - Dk } else if U < poisson.p4 { // centre right // immediate acceptance region R3 = [m, k4+1)*[0, f4), X = m, ... k4 V = U - poisson.p3 if V < 0.0 { k = poisson.k4 - int((U-poisson.p2)/poisson.f4) return } // immediate acceptance region R4 = [k4+1, k5+1)*[0, f5) W = V / poisson.dr if W < poisson.f5 { k = poisson.k5 - int(V/poisson.f5) return } // computation of candidate X > k4, and its counterpart Y < k4 // either squeeze-acceptance of X or acceptance-rejection of Y Dk = int(poisson.dr*poisson.randGenerator.Float64()) + 1 if W <= poisson.f4-float64(Dk)*(poisson.f4-poisson.f4*poisson.f4) { k = poisson.k4 + Dk return } V = poisson.f4 + poisson.f4 - W if V < 1.0 { Y = poisson.k4 - Dk if V <= poisson.f4+float64(Dk)*(1.0-poisson.f4)/poisson.dr { k = Y return } if V <= poisson.f(Y, poisson.l_my, poisson.c_pm) { k = Y return } } X = poisson.k4 + Dk } else { W = poisson.randGenerator.Float64() if U < poisson.p5 { Dk = int(1.0 - math.Log(W)/poisson.ll) X = poisson.k1 - Dk if X < 0 { continue } W *= (U - poisson.p4) * poisson.ll if W <= poisson.f1-float64(Dk)*(poisson.f1-poisson.f1/poisson.r1) { k = X return } } else { Dk = int(1.0 - math.Log(W)/poisson.lr) X = poisson.k5 + Dk W *= (U - poisson.p5) * poisson.lr if W <= poisson.f5-float64(Dk)*(poisson.f5-poisson.f5*poisson.f5) { k = X return } } } // acceptance-rejection test of candidate X from the original area // test, whether W <= f(k), with W = U*h(x) and U -- U(0, 1) // log f(X) = (X - m)*log(my) - log X! + log m! if math.Log(W) <= float64(X)*poisson.l_my-specfunc.LogFactorial(X)-poisson.c_pm { k = X return } } } return }
// Pdf returns the probability distribution function. func (binomial Binomial) Pdf(k int) (p float64) { r := binomial.N - k p = math.Exp(binomial.log_n - specfunc.LogFactorial(k) - specfunc.LogFactorial(r) + binomial.log_p*float64(k) + binomial.log_q*float64(r)) return }