// Copyright ©2017 The Gonum Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package distuv import ( "math" "golang.org/x/exp/rand" "gonum.org/v1/gonum/mathext" ) // Poisson implements the Poisson distribution, a discrete probability distribution // that expresses the probability of a given number of events occurring in a fixed // interval. // The poisson distribution has density function: // // f(k) = λ^k / k! e^(-λ) // // For more information, see https://en.wikipedia.org/wiki/Poisson_distribution. type Poisson struct { // Lambda is the average number of events in an interval. // Lambda must be greater than 0. Lambda float64 Src rand.Source } // CDF computes the value of the cumulative distribution function at x. func (p Poisson) CDF(x float64) float64 { if x < 0 { return 0 } return mathext.GammaIncRegComp(math.Floor(x+1), p.Lambda) } // ExKurtosis returns the excess kurtosis of the distribution. func (p Poisson) ExKurtosis() float64 { return 1 / p.Lambda } // LogProb computes the natural logarithm of the value of the probability // density function at x. func (p Poisson) LogProb(x float64) float64 { if x < 0 || math.Floor(x) != x { return math.Inf(-1) } lg, _ := math.Lgamma(math.Floor(x) + 1) return x*math.Log(p.Lambda) - p.Lambda - lg } // Mean returns the mean of the probability distribution. func (p Poisson) Mean() float64 { return p.Lambda } // NumParameters returns the number of parameters in the distribution. func (Poisson) NumParameters() int { return 1 } // Prob computes the value of the probability density function at x. func (p Poisson) Prob(x float64) float64 { return math.Exp(p.LogProb(x)) } // Rand returns a random sample drawn from the distribution. func (p Poisson) Rand() float64 { // NUMERICAL RECIPES IN C: THE ART OF SCIENTIFIC COMPUTING (ISBN 0-521-43108-5) // p. 294 // rnd := rand.ExpFloat64 var rng *rand.Rand if p.Src != nil { rng = rand.New(p.Src) rnd = rng.ExpFloat64 } if p.Lambda < 10.0 { // Use direct method. var em float64 t := 0.0 for { t += rnd() if t >= p.Lambda { break } em++ } return em } // Generate using: // W. Hörmann. "The transformed rejection method for generating Poisson // random variables." Insurance: Mathematics and Economics // 12.1 (1993): 39-45. // Algorithm PTRS rnd = rand.Float64 if rng != nil { rnd = rng.Float64 } b := 0.931 + 2.53*math.Sqrt(p.Lambda) a := -0.059 + 0.02483*b invalpha := 1.1239 + 1.1328/(b-3.4) vr := 0.9277 - 3.6224/(b-2) for { U := rnd() - 0.5 V := rnd() us := 0.5 - math.Abs(U) k := math.Floor((2*a/us+b)*U + p.Lambda + 0.43) if us >= 0.07 && V <= vr { return k } if k <= 0 || (us < 0.013 && V > us) { continue } lg, _ := math.Lgamma(k + 1) if math.Log(V*invalpha/(a/(us*us)+b)) <= k*math.Log(p.Lambda)-p.Lambda-lg { return k } } } // Skewness returns the skewness of the distribution. func (p Poisson) Skewness() float64 { return 1 / math.Sqrt(p.Lambda) } // StdDev returns the standard deviation of the probability distribution. func (p Poisson) StdDev() float64 { return math.Sqrt(p.Variance()) } // Survival returns the survival function (complementary CDF) at x. func (p Poisson) Survival(x float64) float64 { return 1 - p.CDF(x) } // Variance returns the variance of the probability distribution. func (p Poisson) Variance() float64 { return p.Lambda }