Skip to content

Commit 1ee4291

Browse files
committed
std::rand: implement the chi-squared distribution.
1 parent 1d986de commit 1ee4291

File tree

2 files changed

+99
-2
lines changed

2 files changed

+99
-2
lines changed

src/libstd/rand/distributions/gamma.rs

+98-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11-
//! The Gamma distribution.
11+
//! The Gamma and derived distributions.
1212
1313
use rand::{Rng, Open01};
1414
use super::{IndependentSample, Sample, Exp};
@@ -169,6 +169,103 @@ impl IndependentSample<f64> for GammaLargeShape {
169169
}
170170
}
171171

172+
/// The chi-squared distribution `χ²(k)`, where `k` is the degrees of
173+
/// freedom.
174+
///
175+
/// For `k > 0` integral, this distribution is the sum of the squares
176+
/// of `k` independent standard normal random variables. For other
177+
/// `k`, this uses the equivalent characterisation `χ²(k) = Gamma(k/2,
178+
/// 2)`.
179+
///
180+
/// # Example
181+
///
182+
/// ```rust
183+
/// use std::rand;
184+
/// use std::rand::distributions::{ChiSquared, IndependentSample};
185+
///
186+
/// fn main() {
187+
/// let chi = ChiSquared::new(11.0);
188+
/// let v = chi.ind_sample(&mut rand::task_rng());
189+
/// println!("{} is from a χ²(11) distribution", v)
190+
/// }
191+
/// ```
192+
pub enum ChiSquared {
193+
// k == 1, Gamma(alpha, ..) is particularly slow for alpha < 1,
194+
// e.g. when alpha = 1/2 as it would be for this case, so special-
195+
// casing and using the definition of N(0,1)^2 is faster.
196+
priv DoFExactlyOne,
197+
priv DoFAnythingElse(Gamma)
198+
}
199+
200+
impl ChiSquared {
201+
/// Create a new chi-squared distribution with degrees-of-freedom
202+
/// `k`. Fails if `k < 0`.
203+
pub fn new(k: f64) -> ChiSquared {
204+
if k == 1.0 {
205+
DoFExactlyOne
206+
} else {
207+
assert!(k > 0.0, "ChiSquared::new called with `k` < 0");
208+
DoFAnythingElse(Gamma::new(0.5 * k, 2.0))
209+
}
210+
}
211+
}
212+
impl Sample<f64> for ChiSquared {
213+
fn sample<R: Rng>(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) }
214+
}
215+
impl IndependentSample<f64> for ChiSquared {
216+
fn ind_sample<R: Rng>(&self, rng: &mut R) -> f64 {
217+
match *self {
218+
DoFExactlyOne => {
219+
// k == 1 => N(0,1)^2
220+
let norm = *rng.gen::<StandardNormal>();
221+
norm * norm
222+
}
223+
DoFAnythingElse(ref g) => g.ind_sample(rng)
224+
}
225+
}
226+
}
227+
228+
#[cfg(test)]
229+
mod test {
230+
use rand::*;
231+
use super::*;
232+
use iter::range;
233+
use option::{Some, None};
234+
235+
#[test]
236+
fn test_chi_squared_one() {
237+
let mut chi = ChiSquared::new(1.0);
238+
let mut rng = task_rng();
239+
for _ in range(0, 1000) {
240+
chi.sample(&mut rng);
241+
chi.ind_sample(&mut rng);
242+
}
243+
}
244+
#[test]
245+
fn test_chi_squared_small() {
246+
let mut chi = ChiSquared::new(0.5);
247+
let mut rng = task_rng();
248+
for _ in range(0, 1000) {
249+
chi.sample(&mut rng);
250+
chi.ind_sample(&mut rng);
251+
}
252+
}
253+
#[test]
254+
fn test_chi_squared_large() {
255+
let mut chi = ChiSquared::new(30.0);
256+
let mut rng = task_rng();
257+
for _ in range(0, 1000) {
258+
chi.sample(&mut rng);
259+
chi.ind_sample(&mut rng);
260+
}
261+
}
262+
#[test]
263+
#[should_fail]
264+
fn test_log_normal_invalid_dof() {
265+
ChiSquared::new(-1.0);
266+
}
267+
}
268+
172269
#[cfg(test)]
173270
mod bench {
174271
use super::*;

src/libstd/rand/distributions/mod.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use rand::{Rng, Rand};
2727
use clone::Clone;
2828

2929
pub use self::range::Range;
30-
pub use self::gamma::Gamma;
30+
pub use self::gamma::{Gamma, ChiSquared};
3131
pub use self::normal::{Normal, LogNormal};
3232
pub use self::exponential::Exp;
3333

0 commit comments

Comments
 (0)