1 // Copyright 2018 Developers of the Rand project. 2 // Copyright 2013 The Rust Project Developers. 3 // 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. 9 10 //! The exponential distribution. 11 12 use {Rng}; 13 use distributions::{ziggurat_tables, Distribution}; 14 use distributions::utils::ziggurat; 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] sample<R: Rng + ?Sized>(&self, rng: &mut R) -> f6445 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 /// Note that [`Exp1`][crate::distributions::Exp1] is an optimised implementation for `lambda = 1`. 68 /// 69 /// # Example 70 /// 71 /// ``` 72 /// use rand::distributions::{Exp, Distribution}; 73 /// 74 /// let exp = Exp::new(2.0); 75 /// let v = exp.sample(&mut rand::thread_rng()); 76 /// println!("{} is from a Exp(2) distribution", v); 77 /// ``` 78 #[derive(Clone, Copy, Debug)] 79 pub struct Exp { 80 /// `lambda` stored as `1/lambda`, since this is what we scale by. 81 lambda_inverse: f64 82 } 83 84 impl Exp { 85 /// Construct a new `Exp` with the given shape parameter 86 /// `lambda`. Panics if `lambda <= 0`. 87 #[inline] new(lambda: f64) -> Exp88 pub fn new(lambda: f64) -> Exp { 89 assert!(lambda > 0.0, "Exp::new called with `lambda` <= 0"); 90 Exp { lambda_inverse: 1.0 / lambda } 91 } 92 } 93 94 impl Distribution<f64> for Exp { sample<R: Rng + ?Sized>(&self, rng: &mut R) -> f6495 fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> f64 { 96 let n: f64 = rng.sample(Exp1); 97 n * self.lambda_inverse 98 } 99 } 100 101 #[cfg(test)] 102 mod test { 103 use distributions::Distribution; 104 use super::Exp; 105 106 #[test] test_exp()107 fn test_exp() { 108 let exp = Exp::new(10.0); 109 let mut rng = ::test::rng(221); 110 for _ in 0..1000 { 111 assert!(exp.sample(&mut rng) >= 0.0); 112 } 113 } 114 #[test] 115 #[should_panic] test_exp_invalid_lambda_zero()116 fn test_exp_invalid_lambda_zero() { 117 Exp::new(0.0); 118 } 119 #[test] 120 #[should_panic] test_exp_invalid_lambda_neg()121 fn test_exp_invalid_lambda_neg() { 122 Exp::new(-10.0); 123 } 124 } 125