]>
git.proxmox.com Git - cargo.git/blob - vendor/rand-0.6.5/src/distributions/poisson.rs
1 // Copyright 2018 Developers of the Rand project.
2 // Copyright 2016-2017 The Rust Project Developers.
4 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
5 // https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
6 // <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
7 // option. This file may not be copied, modified, or distributed
8 // except according to those terms.
10 //! The Poisson distribution.
13 use distributions
::{Distribution, Cauchy}
;
14 use distributions
::utils
::log_gamma
;
16 /// The Poisson distribution `Poisson(lambda)`.
18 /// This distribution has a density function:
19 /// `f(k) = lambda^k * exp(-lambda) / k!` for `k >= 0`.
24 /// use rand::distributions::{Poisson, Distribution};
26 /// let poi = Poisson::new(2.0);
27 /// let v = poi.sample(&mut rand::thread_rng());
28 /// println!("{} is from a Poisson(2) distribution", v);
30 #[derive(Clone, Copy, Debug)]
33 // precalculated values
41 /// Construct a new `Poisson` with the given shape parameter
42 /// `lambda`. Panics if `lambda <= 0`.
43 pub fn new(lambda
: f64) -> Poisson
{
44 assert
!(lambda
> 0.0, "Poisson::new called with lambda <= 0");
45 let log_lambda
= lambda
.ln();
48 exp_lambda
: (-lambda
).exp(),
50 sqrt_2lambda
: (2.0 * lambda
).sqrt(),
51 magic_val
: lambda
* log_lambda
- log_gamma(1.0 + lambda
),
56 impl Distribution
<u64> for Poisson
{
57 fn sample
<R
: Rng
+ ?Sized
>(&self, rng
: &mut R
) -> u64 {
58 // using the algorithm from Numerical Recipes in C
60 // for low expected values use the Knuth method
61 if self.lambda
< 12.0 {
64 while p
> self.exp_lambda
{
65 p
*= rng
.gen
::<f64>();
70 // high expected values - rejection method
72 let mut int_result
: u64;
74 // we use the Cauchy distribution as the comparison distribution
76 let cauchy
= Cauchy
::new(0.0, 1.0);
83 // draw from the Cauchy distribution
84 comp_dev
= rng
.sample(cauchy
);
85 // shift the peak of the comparison ditribution
86 result
= self.sqrt_2lambda
* comp_dev
+ self.lambda
;
87 // repeat the drawing until we are in the range of possible values
92 // now the result is a random variable greater than 0 with Cauchy distribution
93 // the result should be an integer value
94 result
= result
.floor();
95 int_result
= result
as u64;
97 // this is the ratio of the Poisson distribution to the comparison distribution
98 // the magic value scales the distribution function to a range of approximately 0-1
99 // since it is not exact, we multiply the ratio by 0.9 to avoid ratios greater than 1
100 // this doesn't change the resulting distribution, only increases the rate of failed drawings
101 let check
= 0.9 * (1.0 + comp_dev
* comp_dev
)
102 * (result
* self.log_lambda
- log_gamma(1.0 + result
) - self.magic_val
).exp();
104 // check with uniform random value - if below the threshold, we are within the target distribution
105 if rng
.gen
::<f64>() <= check
{
116 use distributions
::Distribution
;
120 fn test_poisson_10() {
121 let poisson
= Poisson
::new(10.0);
122 let mut rng
= ::test
::rng(123);
125 sum
+= poisson
.sample(&mut rng
);
127 let avg
= (sum
as f64) / 1000.0;
128 println
!("Poisson average: {}", avg
);
129 assert
!((avg
- 10.0).abs() < 0.5); // not 100% certain, but probable enough
133 fn test_poisson_15() {
134 // Take the 'high expected values' path
135 let poisson
= Poisson
::new(15.0);
136 let mut rng
= ::test
::rng(123);
139 sum
+= poisson
.sample(&mut rng
);
141 let avg
= (sum
as f64) / 1000.0;
142 println
!("Poisson average: {}", avg
);
143 assert
!((avg
- 15.0).abs() < 0.5); // not 100% certain, but probable enough
148 fn test_poisson_invalid_lambda_zero() {
154 fn test_poisson_invalid_lambda_neg() {