]>
Commit | Line | Data |
---|---|---|
b7449926 XL |
1 | // Copyright 2013 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // https://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // https://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
11 | //! The exponential distribution. | |
12 | ||
13 | use {Rng}; | |
14 | use distributions::{ziggurat, ziggurat_tables, Distribution}; | |
15 | ||
16 | /// Samples floating-point numbers according to the exponential distribution, | |
17 | /// with rate parameter `λ = 1`. This is equivalent to `Exp::new(1.0)` or | |
18 | /// sampling with `-rng.gen::<f64>().ln()`, but faster. | |
19 | /// | |
20 | /// See `Exp` for the general exponential distribution. | |
21 | /// | |
22 | /// Implemented via the ZIGNOR variant[^1] of the Ziggurat method. The exact | |
23 | /// description in the paper was adjusted to use tables for the exponential | |
24 | /// distribution rather than normal. | |
25 | /// | |
26 | /// [^1]: Jurgen A. Doornik (2005). [*An Improved Ziggurat Method to | |
27 | /// Generate Normal Random Samples*]( | |
28 | /// https://www.doornik.com/research/ziggurat.pdf). | |
29 | /// Nuffield College, Oxford | |
30 | /// | |
31 | /// # Example | |
32 | /// ``` | |
33 | /// use rand::prelude::*; | |
34 | /// use rand::distributions::Exp1; | |
35 | /// | |
36 | /// let val: f64 = SmallRng::from_entropy().sample(Exp1); | |
37 | /// println!("{}", val); | |
38 | /// ``` | |
39 | #[derive(Clone, Copy, Debug)] | |
40 | pub struct Exp1; | |
41 | ||
42 | // This could be done via `-rng.gen::<f64>().ln()` but that is slower. | |
43 | impl Distribution<f64> for Exp1 { | |
44 | #[inline] | |
45 | fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> f64 { | |
46 | #[inline] | |
47 | fn pdf(x: f64) -> f64 { | |
48 | (-x).exp() | |
49 | } | |
50 | #[inline] | |
51 | fn zero_case<R: Rng + ?Sized>(rng: &mut R, _u: f64) -> f64 { | |
52 | ziggurat_tables::ZIG_EXP_R - rng.gen::<f64>().ln() | |
53 | } | |
54 | ||
55 | ziggurat(rng, false, | |
56 | &ziggurat_tables::ZIG_EXP_X, | |
57 | &ziggurat_tables::ZIG_EXP_F, | |
58 | pdf, zero_case) | |
59 | } | |
60 | } | |
61 | ||
62 | /// The exponential distribution `Exp(lambda)`. | |
63 | /// | |
64 | /// This distribution has density function: `f(x) = lambda * exp(-lambda * x)` | |
65 | /// for `x > 0`. | |
66 | /// | |
67 | /// # Example | |
68 | /// | |
69 | /// ``` | |
70 | /// use rand::distributions::{Exp, Distribution}; | |
71 | /// | |
72 | /// let exp = Exp::new(2.0); | |
73 | /// let v = exp.sample(&mut rand::thread_rng()); | |
74 | /// println!("{} is from a Exp(2) distribution", v); | |
75 | /// ``` | |
76 | #[derive(Clone, Copy, Debug)] | |
77 | pub struct Exp { | |
78 | /// `lambda` stored as `1/lambda`, since this is what we scale by. | |
79 | lambda_inverse: f64 | |
80 | } | |
81 | ||
82 | impl Exp { | |
83 | /// Construct a new `Exp` with the given shape parameter | |
84 | /// `lambda`. Panics if `lambda <= 0`. | |
85 | #[inline] | |
86 | pub fn new(lambda: f64) -> Exp { | |
87 | assert!(lambda > 0.0, "Exp::new called with `lambda` <= 0"); | |
88 | Exp { lambda_inverse: 1.0 / lambda } | |
89 | } | |
90 | } | |
91 | ||
92 | impl Distribution<f64> for Exp { | |
93 | fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> f64 { | |
94 | let n: f64 = rng.sample(Exp1); | |
95 | n * self.lambda_inverse | |
96 | } | |
97 | } | |
98 | ||
99 | #[cfg(test)] | |
100 | mod test { | |
101 | use distributions::Distribution; | |
102 | use super::Exp; | |
103 | ||
104 | #[test] | |
105 | fn test_exp() { | |
106 | let exp = Exp::new(10.0); | |
107 | let mut rng = ::test::rng(221); | |
108 | for _ in 0..1000 { | |
109 | assert!(exp.sample(&mut rng) >= 0.0); | |
110 | } | |
111 | } | |
112 | #[test] | |
113 | #[should_panic] | |
114 | fn test_exp_invalid_lambda_zero() { | |
115 | Exp::new(0.0); | |
116 | } | |
117 | #[test] | |
118 | #[should_panic] | |
119 | fn test_exp_invalid_lambda_neg() { | |
120 | Exp::new(-10.0); | |
121 | } | |
122 | } |