Module rand::distributions::uniform

source ·
Expand description

A distribution uniformly sampling numbers within a given range.

Uniform is the standard distribution to sample uniformly from a range; e.g. Uniform::new_inclusive(1, 6) can sample integers from 1 to 6, like a standard die. Rng::gen_range supports any type supported by Uniform.

This distribution is provided with support for several primitive types (all integer and floating-point types) as well as std::time::Duration, and supports extension to user-defined types via a type-specific back-end implementation.

The types UniformInt, UniformFloat and UniformDuration are the back-ends supporting sampling from primitive integer and floating-point ranges as well as from std::time::Duration; these types do not normally need to be used directly (unless implementing a derived back-end).

Example usage

use rand::{Rng, thread_rng};
use rand::distributions::Uniform;

let mut rng = thread_rng();
let side = Uniform::new(-10.0, 10.0);

// sample between 1 and 10 points
for _ in 0..rng.gen_range(1..=10) {
    // sample a point from the square with sides -10 - 10 in two dimensions
    let (x, y) = (rng.sample(side), rng.sample(side));
    println!("Point: {}, {}", x, y);
}

Extending Uniform to support a custom type

To extend Uniform to support your own types, write a back-end which implements the UniformSampler trait, then implement the SampleUniform helper trait to “register” your back-end. See the MyF32 example below.

At a minimum, the back-end needs to store any parameters needed for sampling (e.g. the target range) and implement new, new_inclusive and sample. Those methods should include an assert to check the range is valid (i.e. low < high). The example below merely wraps another back-end.

The new, new_inclusive and sample_single functions use arguments of type SampleBorrow in order to support passing in values by reference or by value. In the implementation of these functions, you can choose to simply use the reference returned by SampleBorrow::borrow, or you can choose to copy or clone the value, whatever is appropriate for your type.

use rand::prelude::*;
use rand::distributions::uniform::{Uniform, SampleUniform,
        UniformSampler, UniformFloat, SampleBorrow};

struct MyF32(f32);

#[derive(Clone, Copy, Debug)]
struct UniformMyF32(UniformFloat<f32>);

impl UniformSampler for UniformMyF32 {
    type X = MyF32;
    fn new<B1, B2>(low: B1, high: B2) -> Self
        where B1: SampleBorrow<Self::X> + Sized,
              B2: SampleBorrow<Self::X> + Sized
    {
        UniformMyF32(UniformFloat::<f32>::new(low.borrow().0, high.borrow().0))
    }
    fn new_inclusive<B1, B2>(low: B1, high: B2) -> Self
        where B1: SampleBorrow<Self::X> + Sized,
              B2: SampleBorrow<Self::X> + Sized
    {
        UniformMyF32(UniformFloat::<f32>::new_inclusive(
            low.borrow().0,
            high.borrow().0,
        ))
    }
    fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> Self::X {
        MyF32(self.0.sample(rng))
    }
}

impl SampleUniform for MyF32 {
    type Sampler = UniformMyF32;
}

let (low, high) = (MyF32(17.0f32), MyF32(22.0f32));
let uniform = Uniform::new(low, high);
let x = uniform.sample(&mut thread_rng());

Structs

Traits

  • Helper trait similar to Borrow but implemented only for SampleUniform and references to SampleUniform in order to resolve ambiguity issues.
  • Range that supports generating a single sample efficiently.
  • Helper trait for creating objects using the correct implementation of UniformSampler for the sampling type.
  • Helper trait handling actual uniform sampling.