auto merge of #9810 : huonw/rust/rand3, r=alexcrichton
- Adds the `Sample` and `IndependentSample` traits for generating numbers where there are parameters (e.g. a list of elements to draw from, or the mean/variance of a normal distribution). The former takes `&mut self` and the latter takes `&self` (this is the only difference). - Adds proper `Normal` and `Exp`-onential distributions - Adds `Range` which generates `[lo, hi)` generically & properly (via a new trait) replacing the incorrect behaviour of `Rng.gen_integer_range` (this has become `Rng.gen_range` for convenience, it's far more efficient to use `Range` itself) - Move the `Weighted` struct from `std::rand` to `std::rand::distributions` & improve it - optimisations and docs
This commit is contained in:
commit
a4ec8af4c5
@ -318,7 +318,7 @@ fn test_base64_random() {
|
||||
use std::vec;
|
||||
|
||||
do 1000.times {
|
||||
let times = task_rng().gen_integer_range(1u, 100);
|
||||
let times = task_rng().gen_range(1u, 100);
|
||||
let v = vec::from_fn(times, |_| random::<u8>());
|
||||
assert_eq!(v.to_base64(STANDARD).from_base64().unwrap(), v);
|
||||
}
|
||||
|
@ -365,7 +365,7 @@ pub fn test_digest_1million_random<D: Digest>(digest: &mut D, blocksize: uint, e
|
||||
digest.reset();
|
||||
|
||||
while count < total_size {
|
||||
let next: uint = rng.gen_integer_range(0, 2 * blocksize + 1);
|
||||
let next: uint = rng.gen_range(0, 2 * blocksize + 1);
|
||||
let remaining = total_size - count;
|
||||
let size = if next > remaining { remaining } else { next };
|
||||
digest.input(buffer.slice_to(size));
|
||||
|
@ -113,7 +113,7 @@ fn test_flate_round_trip() {
|
||||
let mut r = rand::rng();
|
||||
let mut words = ~[];
|
||||
do 20.times {
|
||||
let range = r.gen_integer_range(1u, 10);
|
||||
let range = r.gen_range(1u, 10);
|
||||
words.push(r.gen_vec::<u8>(range));
|
||||
}
|
||||
do 20.times {
|
||||
|
@ -1069,8 +1069,8 @@ fn isSorted<T:Ord>(arr: &[T]) {
|
||||
isSorted(arr);
|
||||
|
||||
do 3.times {
|
||||
let i1 = rng.gen_integer_range(0u, n);
|
||||
let i2 = rng.gen_integer_range(0u, n);
|
||||
let i1 = rng.gen_range(0u, n);
|
||||
let i2 = rng.gen_range(0u, n);
|
||||
arr.swap(i1, i2);
|
||||
}
|
||||
tim_sort(arr); // 3sort
|
||||
@ -1088,7 +1088,7 @@ fn isSorted<T:Ord>(arr: &[T]) {
|
||||
isSorted(arr);
|
||||
|
||||
do (n/100).times {
|
||||
let idx = rng.gen_integer_range(0u, n);
|
||||
let idx = rng.gen_range(0u, n);
|
||||
arr[idx] = rng.gen();
|
||||
}
|
||||
tim_sort(arr);
|
||||
@ -1141,8 +1141,8 @@ fn isSorted<T:Ord>(arr: &[@T]) {
|
||||
isSorted(arr);
|
||||
|
||||
do 3.times {
|
||||
let i1 = rng.gen_integer_range(0u, n);
|
||||
let i2 = rng.gen_integer_range(0u, n);
|
||||
let i1 = rng.gen_range(0u, n);
|
||||
let i2 = rng.gen_range(0u, n);
|
||||
arr.swap(i1, i2);
|
||||
}
|
||||
tim_sort(arr); // 3sort
|
||||
@ -1160,7 +1160,7 @@ fn isSorted<T:Ord>(arr: &[@T]) {
|
||||
isSorted(arr);
|
||||
|
||||
do (n/100).times {
|
||||
let idx = rng.gen_integer_range(0u, n);
|
||||
let idx = rng.gen_range(0u, n);
|
||||
arr[idx] = @rng.gen();
|
||||
}
|
||||
tim_sort(arr);
|
||||
|
@ -1028,7 +1028,7 @@ fn test_rand_int() {
|
||||
}
|
||||
|
||||
do 30.times {
|
||||
let r = rng.gen_integer_range(0, ctrl.len());
|
||||
let r = rng.gen_range(0, ctrl.len());
|
||||
let (key, _) = ctrl.remove(r);
|
||||
assert!(map.remove(&key));
|
||||
check_structure(&map);
|
||||
|
@ -8,38 +8,226 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Sampling from random distributions
|
||||
/*!
|
||||
Sampling from random distributions.
|
||||
|
||||
// Some implementations use the Ziggurat method
|
||||
// https://en.wikipedia.org/wiki/Ziggurat_algorithm
|
||||
//
|
||||
// The version used here is ZIGNOR [Doornik 2005, "An Improved
|
||||
// Ziggurat Method to Generate Normal Random Samples"] which is slower
|
||||
// (about double, it generates an extra random number) than the
|
||||
// canonical version [Marsaglia & Tsang 2000, "The Ziggurat Method for
|
||||
// Generating Random Variables"], but more robust. If one wanted, one
|
||||
// could implement VIZIGNOR the ZIGNOR paper for more speed.
|
||||
This is a generalization of `Rand` to allow parameters to control the
|
||||
exact properties of the generated values, e.g. the mean and standard
|
||||
deviation of a normal distribution. The `Sample` trait is the most
|
||||
general, and allows for generating values that change some state
|
||||
internally. The `IndependentSample` trait is for generating values
|
||||
that do not need to record state.
|
||||
|
||||
*/
|
||||
|
||||
use iter::range;
|
||||
use option::{Some, None};
|
||||
use num;
|
||||
use rand::{Rng,Rand};
|
||||
use clone::Clone;
|
||||
|
||||
pub use self::range::Range;
|
||||
|
||||
pub mod range;
|
||||
|
||||
/// Types that can be used to create a random instance of `Support`.
|
||||
pub trait Sample<Support> {
|
||||
/// Generate a random value of `Support`, using `rng` as the
|
||||
/// source of randomness.
|
||||
fn sample<R: Rng>(&mut self, rng: &mut R) -> Support;
|
||||
}
|
||||
|
||||
/// `Sample`s that do not require keeping track of state.
|
||||
///
|
||||
/// Since no state is recored, each sample is (statistically)
|
||||
/// independent of all others, assuming the `Rng` used has this
|
||||
/// property.
|
||||
// XXX maybe having this separate is overkill (the only reason is to
|
||||
// take &self rather than &mut self)? or maybe this should be the
|
||||
// trait called `Sample` and the other should be `DependentSample`.
|
||||
pub trait IndependentSample<Support>: Sample<Support> {
|
||||
/// Generate a random value.
|
||||
fn ind_sample<R: Rng>(&self, &mut R) -> Support;
|
||||
}
|
||||
|
||||
/// A wrapper for generating types that implement `Rand` via the
|
||||
/// `Sample` & `IndependentSample` traits.
|
||||
pub struct RandSample<Sup>;
|
||||
|
||||
impl<Sup: Rand> Sample<Sup> for RandSample<Sup> {
|
||||
fn sample<R: Rng>(&mut self, rng: &mut R) -> Sup { self.ind_sample(rng) }
|
||||
}
|
||||
|
||||
impl<Sup: Rand> IndependentSample<Sup> for RandSample<Sup> {
|
||||
fn ind_sample<R: Rng>(&self, rng: &mut R) -> Sup {
|
||||
rng.gen()
|
||||
}
|
||||
}
|
||||
|
||||
/// A value with a particular weight for use with `WeightedChoice`.
|
||||
pub struct Weighted<T> {
|
||||
/// The numerical weight of this item
|
||||
weight: uint,
|
||||
/// The actual item which is being weighted
|
||||
item: T,
|
||||
}
|
||||
|
||||
/// A distribution that selects from a finite collection of weighted items.
|
||||
///
|
||||
/// Each item has an associated weight that influences how likely it
|
||||
/// is to be chosen: higher weight is more likely.
|
||||
///
|
||||
/// The `Clone` restriction is a limitation of the `Sample` and
|
||||
/// `IndepedentSample` traits. Note that `&T` is (cheaply) `Clone` for
|
||||
/// all `T`, as is `uint`, so one can store references or indices into
|
||||
/// another vector.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use std::rand;
|
||||
/// use std::rand::distributions::{Weighted, WeightedChoice, IndepedentSample};
|
||||
///
|
||||
/// fn main() {
|
||||
/// let wc = WeightedChoice::new(~[Weighted { weight: 2, item: 'a' },
|
||||
/// Weighted { weight: 4, item: 'b' },
|
||||
/// Weighted { weight: 1, item: 'c' }]);
|
||||
/// let rng = rand::task_rng();
|
||||
/// for _ in range(0, 16) {
|
||||
/// // on average prints 'a' 4 times, 'b' 8 and 'c' twice.
|
||||
/// println!("{}", wc.ind_sample(rng));
|
||||
/// }
|
||||
/// }
|
||||
/// ```
|
||||
pub struct WeightedChoice<T> {
|
||||
priv items: ~[Weighted<T>],
|
||||
priv weight_range: Range<uint>
|
||||
}
|
||||
|
||||
impl<T: Clone> WeightedChoice<T> {
|
||||
/// Create a new `WeightedChoice`.
|
||||
///
|
||||
/// Fails if:
|
||||
/// - `v` is empty
|
||||
/// - the total weight is 0
|
||||
/// - the total weight is larger than a `uint` can contain.
|
||||
pub fn new(mut items: ~[Weighted<T>]) -> WeightedChoice<T> {
|
||||
// strictly speaking, this is subsumed by the total weight == 0 case
|
||||
assert!(!items.is_empty(), "WeightedChoice::new called with no items");
|
||||
|
||||
let mut running_total = 0u;
|
||||
|
||||
// we convert the list from individual weights to cumulative
|
||||
// weights so we can binary search. This *could* drop elements
|
||||
// with weight == 0 as an optimisation.
|
||||
for item in items.mut_iter() {
|
||||
running_total = running_total.checked_add(&item.weight)
|
||||
.expect("WeightedChoice::new called with a total weight larger \
|
||||
than a uint can contain");
|
||||
|
||||
item.weight = running_total;
|
||||
}
|
||||
assert!(running_total != 0, "WeightedChoice::new called with a total weight of 0");
|
||||
|
||||
WeightedChoice {
|
||||
items: items,
|
||||
// we're likely to be generating numbers in this range
|
||||
// relatively often, so might as well cache it
|
||||
weight_range: Range::new(0, running_total)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone> Sample<T> for WeightedChoice<T> {
|
||||
fn sample<R: Rng>(&mut self, rng: &mut R) -> T { self.ind_sample(rng) }
|
||||
}
|
||||
|
||||
impl<T: Clone> IndependentSample<T> for WeightedChoice<T> {
|
||||
fn ind_sample<R: Rng>(&self, rng: &mut R) -> T {
|
||||
// we want to find the first element that has cumulative
|
||||
// weight > sample_weight, which we do by binary since the
|
||||
// cumulative weights of self.items are sorted.
|
||||
|
||||
// choose a weight in [0, total_weight)
|
||||
let sample_weight = self.weight_range.ind_sample(rng);
|
||||
|
||||
// short circuit when it's the first item
|
||||
if sample_weight < self.items[0].weight {
|
||||
return self.items[0].item.clone();
|
||||
}
|
||||
|
||||
let mut idx = 0;
|
||||
let mut modifier = self.items.len();
|
||||
|
||||
// now we know that every possibility has an element to the
|
||||
// left, so we can just search for the last element that has
|
||||
// cumulative weight <= sample_weight, then the next one will
|
||||
// be "it". (Note that this greatest element will never be the
|
||||
// last element of the vector, since sample_weight is chosen
|
||||
// in [0, total_weight) and the cumulative weight of the last
|
||||
// one is exactly the total weight.)
|
||||
while modifier > 1 {
|
||||
let i = idx + modifier / 2;
|
||||
if self.items[i].weight <= sample_weight {
|
||||
// we're small, so look to the right, but allow this
|
||||
// exact element still.
|
||||
idx = i;
|
||||
// we need the `/ 2` to round up otherwise we'll drop
|
||||
// the trailing elements when `modifier` is odd.
|
||||
modifier += 1;
|
||||
} else {
|
||||
// otherwise we're too big, so go left. (i.e. do
|
||||
// nothing)
|
||||
}
|
||||
modifier /= 2;
|
||||
}
|
||||
return self.items[idx + 1].item.clone();
|
||||
}
|
||||
}
|
||||
|
||||
mod ziggurat_tables;
|
||||
|
||||
// inlining should mean there is no performance penalty for this
|
||||
#[inline]
|
||||
/// Sample a random number using the Ziggurat method (specifically the
|
||||
/// ZIGNOR variant from Doornik 2005). Most of the arguments are
|
||||
/// directly from the paper:
|
||||
///
|
||||
/// * `rng`: source of randomness
|
||||
/// * `symmetric`: whether this is a symmetric distribution, or one-sided with P(x < 0) = 0.
|
||||
/// * `X`: the $x_i$ abscissae.
|
||||
/// * `F`: precomputed values of the PDF at the $x_i$, (i.e. $f(x_i)$)
|
||||
/// * `F_DIFF`: precomputed values of $f(x_i) - f(x_{i+1})$
|
||||
/// * `pdf`: the probability density function
|
||||
/// * `zero_case`: manual sampling from the tail when we chose the
|
||||
/// bottom box (i.e. i == 0)
|
||||
|
||||
// the perf improvement (25-50%) is definitely worth the extra code
|
||||
// size from force-inlining.
|
||||
#[inline(always)]
|
||||
fn ziggurat<R:Rng>(rng: &mut R,
|
||||
center_u: bool,
|
||||
symmetric: bool,
|
||||
X: ziggurat_tables::ZigTable,
|
||||
F: ziggurat_tables::ZigTable,
|
||||
F_DIFF: ziggurat_tables::ZigTable,
|
||||
pdf: &'static fn(f64) -> f64, // probability density function
|
||||
pdf: &'static fn(f64) -> f64,
|
||||
zero_case: &'static fn(&mut R, f64) -> f64) -> f64 {
|
||||
static SCALE: f64 = (1u64 << 53) as f64;
|
||||
loop {
|
||||
let u = if center_u {2.0 * rng.gen() - 1.0} else {rng.gen()};
|
||||
let i: uint = rng.gen::<uint>() & 0xff;
|
||||
// reimplement the f64 generation as an optimisation suggested
|
||||
// by the Doornik paper: we have a lot of precision-space
|
||||
// (i.e. there are 11 bits of the 64 of a u64 to use after
|
||||
// creating a f64), so we might as well reuse some to save
|
||||
// generating a whole extra random number. (Seems to be 15%
|
||||
// faster.)
|
||||
let bits: u64 = rng.gen();
|
||||
let i = (bits & 0xff) as uint;
|
||||
let f = (bits >> 11) as f64 / SCALE;
|
||||
|
||||
// u is either U(-1, 1) or U(0, 1) depending on if this is a
|
||||
// symmetric distribution or not.
|
||||
let u = if symmetric {2.0 * f - 1.0} else {f};
|
||||
let x = u * X[i];
|
||||
|
||||
let test_x = if center_u {num::abs(x)} else {x};
|
||||
let test_x = if symmetric {num::abs(x)} else {x};
|
||||
|
||||
// algebraically equivalent to |u| < X[i+1]/X[i] (or u < X[i+1]/X[i])
|
||||
if test_x < X[i + 1] {
|
||||
@ -49,30 +237,25 @@ fn ziggurat<R:Rng>(rng: &mut R,
|
||||
return zero_case(rng, u);
|
||||
}
|
||||
// algebraically equivalent to f1 + DRanU()*(f0 - f1) < 1
|
||||
if F[i+1] + F_DIFF[i+1] * rng.gen() < pdf(x) {
|
||||
if F[i + 1] + F_DIFF[i + 1] * rng.gen() < pdf(x) {
|
||||
return x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper around an `f64` to generate N(0, 1) random numbers (a.k.a. a
|
||||
/// standard normal, or Gaussian). Multiplying the generated values by the
|
||||
/// desired standard deviation `sigma` then adding the desired mean `mu` will
|
||||
/// give N(mu, sigma^2) distributed random numbers.
|
||||
/// A wrapper around an `f64` to generate N(0, 1) random numbers
|
||||
/// (a.k.a. a standard normal, or Gaussian).
|
||||
///
|
||||
/// Note that this has to be unwrapped before use as an `f64` (using either
|
||||
/// `*` or `cast::transmute` is safe).
|
||||
/// See `Normal` for the general normal distribution. That this has to
|
||||
/// be unwrapped before use as an `f64` (using either `*` or
|
||||
/// `cast::transmute` is safe).
|
||||
///
|
||||
/// # Example
|
||||
/// Implemented via the ZIGNOR variant[1] of the Ziggurat method.
|
||||
///
|
||||
/// ```
|
||||
/// use std::rand::distributions::StandardNormal;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let normal = 2.0 + (*rand::random::<StandardNormal>()) * 3.0;
|
||||
/// println!("{} is from a N(2, 9) distribution", normal)
|
||||
/// }
|
||||
/// ```
|
||||
/// [1]: Jurgen A. Doornik (2005). [*An Improved Ziggurat Method to
|
||||
/// Generate Normal Random
|
||||
/// Samples*](http://www.doornik.com/research/ziggurat.pdf). Nuffield
|
||||
/// College, Oxford
|
||||
pub struct StandardNormal(f64);
|
||||
|
||||
impl Rand for StandardNormal {
|
||||
@ -110,23 +293,62 @@ fn zero_case<R:Rng>(rng: &mut R, u: f64) -> f64 {
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper around an `f64` to generate Exp(1) random numbers. Dividing by
|
||||
/// the desired rate `lambda` will give Exp(lambda) distributed random
|
||||
/// numbers.
|
||||
/// The normal distribution `N(mean, std_dev**2)`.
|
||||
///
|
||||
/// Note that this has to be unwrapped before use as an `f64` (using either
|
||||
/// `*` or `cast::transmute` is safe).
|
||||
/// This uses the ZIGNOR variant of the Ziggurat method, see
|
||||
/// `StandardNormal` for more details.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use std::rand::distributions::Exp1;
|
||||
/// use std::rand;
|
||||
/// use std::rand::distributions::{Normal, IndependentSample};
|
||||
///
|
||||
/// fn main() {
|
||||
/// let exp2 = (*rand::random::<Exp1>()) * 0.5;
|
||||
/// println!("{} is from a Exp(2) distribution", exp2);
|
||||
/// let normal = Normal::new(2.0, 3.0);
|
||||
/// let v = normal.ind_sample(rand::task_rng());
|
||||
/// println!("{} is from a N(2, 9) distribution", v)
|
||||
/// }
|
||||
/// ```
|
||||
pub struct Normal {
|
||||
priv mean: f64,
|
||||
priv std_dev: f64
|
||||
}
|
||||
|
||||
impl Normal {
|
||||
/// Construct a new `Normal` distribution with the given mean and
|
||||
/// standard deviation. Fails if `std_dev < 0`.
|
||||
pub fn new(mean: f64, std_dev: f64) -> Normal {
|
||||
assert!(std_dev >= 0.0, "Normal::new called with `std_dev` < 0");
|
||||
Normal {
|
||||
mean: mean,
|
||||
std_dev: std_dev
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Sample<f64> for Normal {
|
||||
fn sample<R: Rng>(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) }
|
||||
}
|
||||
impl IndependentSample<f64> for Normal {
|
||||
fn ind_sample<R: Rng>(&self, rng: &mut R) -> f64 {
|
||||
self.mean + self.std_dev * (*rng.gen::<StandardNormal>())
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper around an `f64` to generate Exp(1) random numbers.
|
||||
///
|
||||
/// See `Exp` for the general exponential distribution.Note that this
|
||||
// has to be unwrapped before use as an `f64` (using either
|
||||
/// `*` or `cast::transmute` is safe).
|
||||
///
|
||||
/// Implemented via the ZIGNOR variant[1] of the Ziggurat method. The
|
||||
/// exact description in the paper was adjusted to use tables for the
|
||||
/// exponential distribution rather than normal.
|
||||
///
|
||||
/// [1]: Jurgen A. Doornik (2005). [*An Improved Ziggurat Method to
|
||||
/// Generate Normal Random
|
||||
/// Samples*](http://www.doornik.com/research/ziggurat.pdf). Nuffield
|
||||
/// College, Oxford
|
||||
pub struct Exp1(f64);
|
||||
|
||||
// This could be done via `-rng.gen::<f64>().ln()` but that is slower.
|
||||
@ -148,3 +370,221 @@ fn zero_case<R:Rng>(rng: &mut R, _u: f64) -> f64 {
|
||||
pdf, zero_case))
|
||||
}
|
||||
}
|
||||
|
||||
/// The exponential distribution `Exp(lambda)`.
|
||||
///
|
||||
/// This distribution has density function: `f(x) = lambda *
|
||||
/// exp(-lambda * x)` for `x > 0`.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```
|
||||
/// use std::rand;
|
||||
/// use std::rand::distributions::{Exp, IndependentSample};
|
||||
///
|
||||
/// fn main() {
|
||||
/// let exp = Exp::new(2.0);
|
||||
/// let v = exp.ind_sample(rand::task_rng());
|
||||
/// println!("{} is from a Exp(2) distribution", v);
|
||||
/// }
|
||||
/// ```
|
||||
pub struct Exp {
|
||||
/// `lambda` stored as `1/lambda`, since this is what we scale by.
|
||||
priv lambda_inverse: f64
|
||||
}
|
||||
|
||||
impl Exp {
|
||||
/// Construct a new `Exp` with the given shape parameter
|
||||
/// `lambda`. Fails if `lambda <= 0`.
|
||||
pub fn new(lambda: f64) -> Exp {
|
||||
assert!(lambda > 0.0, "Exp::new called with `lambda` <= 0");
|
||||
Exp { lambda_inverse: 1.0 / lambda }
|
||||
}
|
||||
}
|
||||
|
||||
impl Sample<f64> for Exp {
|
||||
fn sample<R: Rng>(&mut self, rng: &mut R) -> f64 { self.ind_sample(rng) }
|
||||
}
|
||||
impl IndependentSample<f64> for Exp {
|
||||
fn ind_sample<R: Rng>(&self, rng: &mut R) -> f64 {
|
||||
(*rng.gen::<Exp1>()) * self.lambda_inverse
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use rand::*;
|
||||
use super::*;
|
||||
use iter::range;
|
||||
use option::{Some, None};
|
||||
|
||||
struct ConstRand(uint);
|
||||
impl Rand for ConstRand {
|
||||
fn rand<R: Rng>(_: &mut R) -> ConstRand {
|
||||
ConstRand(0)
|
||||
}
|
||||
}
|
||||
|
||||
// 0, 1, 2, 3, ...
|
||||
struct CountingRng { i: u32 }
|
||||
impl Rng for CountingRng {
|
||||
fn next_u32(&mut self) -> u32 {
|
||||
self.i += 1;
|
||||
self.i - 1
|
||||
}
|
||||
fn next_u64(&mut self) -> u64 {
|
||||
self.next_u32() as u64
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_rand_sample() {
|
||||
let mut rand_sample = RandSample::<ConstRand>;
|
||||
|
||||
assert_eq!(*rand_sample.sample(task_rng()), 0);
|
||||
assert_eq!(*rand_sample.ind_sample(task_rng()), 0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_normal() {
|
||||
let mut norm = Normal::new(10.0, 10.0);
|
||||
let rng = task_rng();
|
||||
for _ in range(0, 1000) {
|
||||
norm.sample(rng);
|
||||
norm.ind_sample(rng);
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
#[should_fail]
|
||||
fn test_normal_invalid_sd() {
|
||||
Normal::new(10.0, -1.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_exp() {
|
||||
let mut exp = Exp::new(10.0);
|
||||
let rng = task_rng();
|
||||
for _ in range(0, 1000) {
|
||||
assert!(exp.sample(rng) >= 0.0);
|
||||
assert!(exp.ind_sample(rng) >= 0.0);
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
#[should_fail]
|
||||
fn test_exp_invalid_lambda_zero() {
|
||||
Exp::new(0.0);
|
||||
}
|
||||
#[test]
|
||||
#[should_fail]
|
||||
fn test_exp_invalid_lambda_neg() {
|
||||
Exp::new(-10.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_weighted_choice() {
|
||||
// this makes assumptions about the internal implementation of
|
||||
// WeightedChoice, specifically: it doesn't reorder the items,
|
||||
// it doesn't do weird things to the RNG (so 0 maps to 0, 1 to
|
||||
// 1, internally; modulo a modulo operation).
|
||||
|
||||
macro_rules! t (
|
||||
($items:expr, $expected:expr) => {{
|
||||
let wc = WeightedChoice::new($items);
|
||||
let expected = $expected;
|
||||
|
||||
let mut rng = CountingRng { i: 0 };
|
||||
|
||||
for &val in expected.iter() {
|
||||
assert_eq!(wc.ind_sample(&mut rng), val)
|
||||
}
|
||||
}}
|
||||
);
|
||||
|
||||
t!(~[Weighted { weight: 1, item: 10}], ~[10]);
|
||||
|
||||
// skip some
|
||||
t!(~[Weighted { weight: 0, item: 20},
|
||||
Weighted { weight: 2, item: 21},
|
||||
Weighted { weight: 0, item: 22},
|
||||
Weighted { weight: 1, item: 23}],
|
||||
~[21,21, 23]);
|
||||
|
||||
// different weights
|
||||
t!(~[Weighted { weight: 4, item: 30},
|
||||
Weighted { weight: 3, item: 31}],
|
||||
~[30,30,30,30, 31,31,31]);
|
||||
|
||||
// check that we're binary searching
|
||||
// correctly with some vectors of odd
|
||||
// length.
|
||||
t!(~[Weighted { weight: 1, item: 40},
|
||||
Weighted { weight: 1, item: 41},
|
||||
Weighted { weight: 1, item: 42},
|
||||
Weighted { weight: 1, item: 43},
|
||||
Weighted { weight: 1, item: 44}],
|
||||
~[40, 41, 42, 43, 44]);
|
||||
t!(~[Weighted { weight: 1, item: 50},
|
||||
Weighted { weight: 1, item: 51},
|
||||
Weighted { weight: 1, item: 52},
|
||||
Weighted { weight: 1, item: 53},
|
||||
Weighted { weight: 1, item: 54},
|
||||
Weighted { weight: 1, item: 55},
|
||||
Weighted { weight: 1, item: 56}],
|
||||
~[50, 51, 52, 53, 54, 55, 56]);
|
||||
}
|
||||
|
||||
#[test] #[should_fail]
|
||||
fn test_weighted_choice_no_items() {
|
||||
WeightedChoice::<int>::new(~[]);
|
||||
}
|
||||
#[test] #[should_fail]
|
||||
fn test_weighted_choice_zero_weight() {
|
||||
WeightedChoice::new(~[Weighted { weight: 0, item: 0},
|
||||
Weighted { weight: 0, item: 1}]);
|
||||
}
|
||||
#[test] #[should_fail]
|
||||
fn test_weighted_choice_weight_overflows() {
|
||||
let x = (-1) as uint / 2; // x + x + 2 is the overflow
|
||||
WeightedChoice::new(~[Weighted { weight: x, item: 0 },
|
||||
Weighted { weight: 1, item: 1 },
|
||||
Weighted { weight: x, item: 2 },
|
||||
Weighted { weight: 1, item: 3 }]);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod bench {
|
||||
use extra::test::BenchHarness;
|
||||
use rand::*;
|
||||
use super::*;
|
||||
use iter::range;
|
||||
use option::{Some, None};
|
||||
use mem::size_of;
|
||||
|
||||
static N: u64 = 100;
|
||||
|
||||
#[bench]
|
||||
fn rand_normal(bh: &mut BenchHarness) {
|
||||
let mut rng = XorShiftRng::new();
|
||||
let mut normal = Normal::new(-2.71828, 3.14159);
|
||||
|
||||
do bh.iter {
|
||||
for _ in range(0, N) {
|
||||
normal.sample(&mut rng);
|
||||
}
|
||||
}
|
||||
bh.bytes = size_of::<f64>() as u64 * N;
|
||||
}
|
||||
#[bench]
|
||||
fn rand_exp(bh: &mut BenchHarness) {
|
||||
let mut rng = XorShiftRng::new();
|
||||
let mut exp = Exp::new(2.71828 * 3.14159);
|
||||
|
||||
do bh.iter {
|
||||
for _ in range(0, N) {
|
||||
exp.sample(&mut rng);
|
||||
}
|
||||
}
|
||||
bh.bytes = size_of::<f64>() as u64 * N;
|
||||
}
|
||||
}
|
||||
|
@ -19,10 +19,15 @@
|
||||
static RAND_SIZE_LEN: u32 = 8;
|
||||
static RAND_SIZE: u32 = 1 << RAND_SIZE_LEN;
|
||||
|
||||
/// A random number generator that uses the [ISAAC
|
||||
/// algorithm](http://en.wikipedia.org/wiki/ISAAC_%28cipher%29).
|
||||
/// A random number generator that uses the ISAAC algorithm[1].
|
||||
///
|
||||
/// The ISAAC algorithm is suitable for cryptographic purposes.
|
||||
/// The ISAAC algorithm is generally accepted as suitable for
|
||||
/// cryptographic purposes, but this implementation has not be
|
||||
/// verified as such. Prefer a generator like `OSRng` that defers to
|
||||
/// the operating system for cases that need high security.
|
||||
///
|
||||
/// [1]: Bob Jenkins, [*ISAAC: A fast cryptographic random number
|
||||
/// generator*](http://www.burtleburtle.net/bob/rand/isaacafa.html)
|
||||
pub struct IsaacRng {
|
||||
priv cnt: u32,
|
||||
priv rsl: [u32, .. RAND_SIZE],
|
||||
@ -216,11 +221,16 @@ fn from_seed(seed: &'self [u32]) -> IsaacRng {
|
||||
static RAND_SIZE_64_LEN: uint = 8;
|
||||
static RAND_SIZE_64: uint = 1 << RAND_SIZE_64_LEN;
|
||||
|
||||
/// A random number generator that uses the 64-bit variant of the
|
||||
/// [ISAAC
|
||||
/// algorithm](http://en.wikipedia.org/wiki/ISAAC_%28cipher%29).
|
||||
/// A random number generator that uses ISAAC-64[1], the 64-bit
|
||||
/// variant of the ISAAC algorithm.
|
||||
///
|
||||
/// The ISAAC algorithm is suitable for cryptographic purposes.
|
||||
/// The ISAAC algorithm is generally accepted as suitable for
|
||||
/// cryptographic purposes, but this implementation has not be
|
||||
/// verified as such. Prefer a generator like `OSRng` that defers to
|
||||
/// the operating system for cases that need high security.
|
||||
///
|
||||
/// [1]: Bob Jenkins, [*ISAAC: A fast cryptographic random number
|
||||
/// generator*](http://www.burtleburtle.net/bob/rand/isaacafa.html)
|
||||
pub struct Isaac64Rng {
|
||||
priv cnt: uint,
|
||||
priv rsl: [u64, .. RAND_SIZE_64],
|
||||
|
@ -28,6 +28,23 @@
|
||||
Unix systems, and will automatically reseed itself from this source
|
||||
after generating 32 KiB of random data.
|
||||
|
||||
# Cryptographic security
|
||||
|
||||
An application that requires random numbers for cryptographic purposes
|
||||
should prefer `OSRng`, which reads randomness from one of the source
|
||||
that the operating system provides (e.g. `/dev/urandom` on
|
||||
Unixes). The other random number generators provided by this module
|
||||
are either known to be insecure (`XorShiftRng`), or are not verified
|
||||
to be secure (`IsaacRng`, `Isaac64Rng` and `StdRng`).
|
||||
|
||||
*Note*: on Linux, `/dev/random` is more secure than `/dev/urandom`,
|
||||
but it is a blocking RNG, and will wait until it has determined that
|
||||
it has collected enough entropy to fulfill a request for random
|
||||
data. It can be used with the `Rng` trait provided by this module by
|
||||
opening the file and passing it to `reader::ReaderRng`. Since it
|
||||
blocks, `/dev/random` should only be used to retrieve small amounts of
|
||||
randomness.
|
||||
|
||||
# Examples
|
||||
|
||||
```rust
|
||||
@ -53,17 +70,20 @@ fn main () {
|
||||
*/
|
||||
|
||||
use cast;
|
||||
use cmp::Ord;
|
||||
use container::Container;
|
||||
use iter::{Iterator, range};
|
||||
use local_data;
|
||||
use prelude::*;
|
||||
use str;
|
||||
use u64;
|
||||
use vec;
|
||||
|
||||
pub use self::isaac::{IsaacRng, Isaac64Rng};
|
||||
pub use self::os::OSRng;
|
||||
|
||||
use self::distributions::{Range, IndependentSample};
|
||||
use self::distributions::range::SampleRange;
|
||||
|
||||
pub mod distributions;
|
||||
pub mod isaac;
|
||||
pub mod os;
|
||||
@ -78,14 +98,6 @@ pub trait Rand {
|
||||
fn rand<R: Rng>(rng: &mut R) -> Self;
|
||||
}
|
||||
|
||||
/// A value with a particular weight compared to other values
|
||||
pub struct Weighted<T> {
|
||||
/// The numerical weight of this item
|
||||
weight: uint,
|
||||
/// The actual item which is being weighted
|
||||
item: T,
|
||||
}
|
||||
|
||||
/// A random number generator
|
||||
pub trait Rng {
|
||||
/// Return the next random u32. This rarely needs to be called
|
||||
@ -196,14 +208,14 @@ fn gen_vec<T: Rand>(&mut self, len: uint) -> ~[T] {
|
||||
vec::from_fn(len, |_| self.gen())
|
||||
}
|
||||
|
||||
/// Generate a random primitive integer in the range [`low`,
|
||||
/// `high`). Fails if `low >= high`.
|
||||
/// Generate a random value in the range [`low`, `high`). Fails if
|
||||
/// `low >= high`.
|
||||
///
|
||||
/// This gives a uniform distribution (assuming this RNG is itself
|
||||
/// uniform), even for edge cases like `gen_integer_range(0u8,
|
||||
/// 170)`, which a naive modulo operation would return numbers
|
||||
/// less than 85 with double the probability to those greater than
|
||||
/// 85.
|
||||
/// This is a convenience wrapper around
|
||||
/// `distributions::Range`. If this function will be called
|
||||
/// repeatedly with the same arguments, one should use `Range`, as
|
||||
/// that will amortize the computations that allow for perfect
|
||||
/// uniformity, as they only happen on initialization.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
@ -213,22 +225,15 @@ fn gen_vec<T: Rand>(&mut self, len: uint) -> ~[T] {
|
||||
///
|
||||
/// fn main() {
|
||||
/// let mut rng = rand::task_rng();
|
||||
/// let n: uint = rng.gen_integer_range(0u, 10);
|
||||
/// let n: uint = rng.gen_range(0u, 10);
|
||||
/// println!("{}", n);
|
||||
/// let m: int = rng.gen_integer_range(-40, 400);
|
||||
/// let m: float = rng.gen_range(-40.0, 1.3e5);
|
||||
/// println!("{}", m);
|
||||
/// }
|
||||
/// ```
|
||||
fn gen_integer_range<T: Rand + Int>(&mut self, low: T, high: T) -> T {
|
||||
assert!(low < high, "RNG.gen_integer_range called with low >= high");
|
||||
let range = (high - low).to_u64().unwrap();
|
||||
let accept_zone = u64::max_value - u64::max_value % range;
|
||||
loop {
|
||||
let rand = self.gen::<u64>();
|
||||
if rand < accept_zone {
|
||||
return low + NumCast::from(rand % range).unwrap();
|
||||
}
|
||||
}
|
||||
fn gen_range<T: Ord + SampleRange>(&mut self, low: T, high: T) -> T {
|
||||
assert!(low < high, "Rng.gen_range called with low >= high");
|
||||
Range::new(low, high).ind_sample(self)
|
||||
}
|
||||
|
||||
/// Return a bool with a 1 in n chance of true
|
||||
@ -245,7 +250,7 @@ fn gen_integer_range<T: Rand + Int>(&mut self, low: T, high: T) -> T {
|
||||
/// }
|
||||
/// ```
|
||||
fn gen_weighted_bool(&mut self, n: uint) -> bool {
|
||||
n == 0 || self.gen_integer_range(0, n) == 0
|
||||
n == 0 || self.gen_range(0, n) == 0
|
||||
}
|
||||
|
||||
/// Return a random string of the specified length composed of
|
||||
@ -295,95 +300,10 @@ fn choose_option<'a, T>(&mut self, values: &'a [T]) -> Option<&'a T> {
|
||||
if values.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(&values[self.gen_integer_range(0u, values.len())])
|
||||
Some(&values[self.gen_range(0u, values.len())])
|
||||
}
|
||||
}
|
||||
|
||||
/// Choose an item respecting the relative weights, failing if the sum of
|
||||
/// the weights is 0
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use std::rand;
|
||||
/// use std::rand::Rng;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let mut rng = rand::rng();
|
||||
/// let x = [rand::Weighted {weight: 4, item: 'a'},
|
||||
/// rand::Weighted {weight: 2, item: 'b'},
|
||||
/// rand::Weighted {weight: 2, item: 'c'}];
|
||||
/// println!("{}", rng.choose_weighted(x));
|
||||
/// }
|
||||
/// ```
|
||||
fn choose_weighted<T:Clone>(&mut self, v: &[Weighted<T>]) -> T {
|
||||
self.choose_weighted_option(v).expect("Rng.choose_weighted: total weight is 0")
|
||||
}
|
||||
|
||||
/// Choose Some(item) respecting the relative weights, returning none if
|
||||
/// the sum of the weights is 0
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use std::rand;
|
||||
/// use std::rand::Rng;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let mut rng = rand::rng();
|
||||
/// let x = [rand::Weighted {weight: 4, item: 'a'},
|
||||
/// rand::Weighted {weight: 2, item: 'b'},
|
||||
/// rand::Weighted {weight: 2, item: 'c'}];
|
||||
/// println!("{:?}", rng.choose_weighted_option(x));
|
||||
/// }
|
||||
/// ```
|
||||
fn choose_weighted_option<T:Clone>(&mut self, v: &[Weighted<T>])
|
||||
-> Option<T> {
|
||||
let mut total = 0u;
|
||||
for item in v.iter() {
|
||||
total += item.weight;
|
||||
}
|
||||
if total == 0u {
|
||||
return None;
|
||||
}
|
||||
let chosen = self.gen_integer_range(0u, total);
|
||||
let mut so_far = 0u;
|
||||
for item in v.iter() {
|
||||
so_far += item.weight;
|
||||
if so_far > chosen {
|
||||
return Some(item.item.clone());
|
||||
}
|
||||
}
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
/// Return a vec containing copies of the items, in order, where
|
||||
/// the weight of the item determines how many copies there are
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use std::rand;
|
||||
/// use std::rand::Rng;
|
||||
///
|
||||
/// fn main() {
|
||||
/// let mut rng = rand::rng();
|
||||
/// let x = [rand::Weighted {weight: 4, item: 'a'},
|
||||
/// rand::Weighted {weight: 2, item: 'b'},
|
||||
/// rand::Weighted {weight: 2, item: 'c'}];
|
||||
/// println!("{}", rng.weighted_vec(x));
|
||||
/// }
|
||||
/// ```
|
||||
fn weighted_vec<T:Clone>(&mut self, v: &[Weighted<T>]) -> ~[T] {
|
||||
let mut r = ~[];
|
||||
for item in v.iter() {
|
||||
for _ in range(0u, item.weight) {
|
||||
r.push(item.item.clone());
|
||||
}
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
/// Shuffle a vec
|
||||
///
|
||||
/// # Example
|
||||
@ -425,7 +345,7 @@ fn shuffle_mut<T>(&mut self, values: &mut [T]) {
|
||||
// invariant: elements with index >= i have been locked in place.
|
||||
i -= 1u;
|
||||
// lock element i in place.
|
||||
values.swap(i, self.gen_integer_range(0u, i + 1u));
|
||||
values.swap(i, self.gen_range(0u, i + 1u));
|
||||
}
|
||||
}
|
||||
|
||||
@ -451,7 +371,7 @@ fn sample<A, T: Iterator<A>>(&mut self, iter: T, n: uint) -> ~[A] {
|
||||
continue
|
||||
}
|
||||
|
||||
let k = self.gen_integer_range(0, i + 1);
|
||||
let k = self.gen_range(0, i + 1);
|
||||
if k < reservoir.len() {
|
||||
reservoir[k] = elem
|
||||
}
|
||||
@ -498,8 +418,8 @@ pub trait SeedableRng<Seed>: Rng {
|
||||
|
||||
/// Create a random number generator with a default algorithm and seed.
|
||||
///
|
||||
/// It returns the cryptographically-safest `Rng` algorithm currently
|
||||
/// available in Rust. If you require a specifically seeded `Rng` for
|
||||
/// It returns the strongest `Rng` algorithm currently implemented in
|
||||
/// pure Rust. If you require a specifically seeded `Rng` for
|
||||
/// consistency over time you should pick one algorithm and create the
|
||||
/// `Rng` yourself.
|
||||
///
|
||||
@ -574,12 +494,16 @@ pub fn weak_rng() -> XorShiftRng {
|
||||
XorShiftRng::new()
|
||||
}
|
||||
|
||||
/// An [Xorshift random number
|
||||
/// generator](http://en.wikipedia.org/wiki/Xorshift).
|
||||
/// An Xorshift[1] random number
|
||||
/// generator.
|
||||
///
|
||||
/// The Xorshift algorithm is not suitable for cryptographic purposes
|
||||
/// but is very fast. If you do not know for sure that it fits your
|
||||
/// requirements, use a more secure one such as `IsaacRng`.
|
||||
/// requirements, use a more secure one such as `IsaacRng` or `OSRng`.
|
||||
///
|
||||
/// [1]: Marsaglia, George (July 2003). ["Xorshift
|
||||
/// RNGs"](http://www.jstatsoft.org/v08/i14/paper). *Journal of
|
||||
/// Statistical Software*. Vol. 8 (Issue 14).
|
||||
pub struct XorShiftRng {
|
||||
priv x: u32,
|
||||
priv y: u32,
|
||||
@ -759,36 +683,36 @@ fn test_fill_bytes_default() {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_gen_integer_range() {
|
||||
fn test_gen_range() {
|
||||
let mut r = rng();
|
||||
for _ in range(0, 1000) {
|
||||
let a = r.gen_integer_range(-3i, 42);
|
||||
let a = r.gen_range(-3i, 42);
|
||||
assert!(a >= -3 && a < 42);
|
||||
assert_eq!(r.gen_integer_range(0, 1), 0);
|
||||
assert_eq!(r.gen_integer_range(-12, -11), -12);
|
||||
assert_eq!(r.gen_range(0, 1), 0);
|
||||
assert_eq!(r.gen_range(-12, -11), -12);
|
||||
}
|
||||
|
||||
for _ in range(0, 1000) {
|
||||
let a = r.gen_integer_range(10, 42);
|
||||
let a = r.gen_range(10, 42);
|
||||
assert!(a >= 10 && a < 42);
|
||||
assert_eq!(r.gen_integer_range(0, 1), 0);
|
||||
assert_eq!(r.gen_integer_range(3_000_000u, 3_000_001), 3_000_000);
|
||||
assert_eq!(r.gen_range(0, 1), 0);
|
||||
assert_eq!(r.gen_range(3_000_000u, 3_000_001), 3_000_000);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_fail]
|
||||
fn test_gen_integer_range_fail_int() {
|
||||
fn test_gen_range_fail_int() {
|
||||
let mut r = rng();
|
||||
r.gen_integer_range(5i, -2);
|
||||
r.gen_range(5i, -2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_fail]
|
||||
fn test_gen_integer_range_fail_uint() {
|
||||
fn test_gen_range_fail_uint() {
|
||||
let mut r = rng();
|
||||
r.gen_integer_range(5u, 2u);
|
||||
r.gen_range(5u, 2u);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -842,44 +766,6 @@ fn test_choose_option() {
|
||||
assert_eq!(r.choose_option(v), Some(&i));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_choose_weighted() {
|
||||
let mut r = rng();
|
||||
assert!(r.choose_weighted([
|
||||
Weighted { weight: 1u, item: 42 },
|
||||
]) == 42);
|
||||
assert!(r.choose_weighted([
|
||||
Weighted { weight: 0u, item: 42 },
|
||||
Weighted { weight: 1u, item: 43 },
|
||||
]) == 43);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_choose_weighted_option() {
|
||||
let mut r = rng();
|
||||
assert!(r.choose_weighted_option([
|
||||
Weighted { weight: 1u, item: 42 },
|
||||
]) == Some(42));
|
||||
assert!(r.choose_weighted_option([
|
||||
Weighted { weight: 0u, item: 42 },
|
||||
Weighted { weight: 1u, item: 43 },
|
||||
]) == Some(43));
|
||||
let v: Option<int> = r.choose_weighted_option([]);
|
||||
assert!(v.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_weighted_vec() {
|
||||
let mut r = rng();
|
||||
let empty: ~[int] = ~[];
|
||||
assert_eq!(r.weighted_vec([]), empty);
|
||||
assert!(r.weighted_vec([
|
||||
Weighted { weight: 0u, item: 3u },
|
||||
Weighted { weight: 1u, item: 2u },
|
||||
Weighted { weight: 2u, item: 1u },
|
||||
]) == ~[2u, 1u, 1u]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_shuffle() {
|
||||
let mut r = rng();
|
||||
@ -893,7 +779,7 @@ fn test_task_rng() {
|
||||
let mut r = task_rng();
|
||||
r.gen::<int>();
|
||||
assert_eq!(r.shuffle(~[1, 1, 1]), ~[1, 1, 1]);
|
||||
assert_eq!(r.gen_integer_range(0u, 1u), 0u);
|
||||
assert_eq!(r.gen_range(0u, 1u), 0u);
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -952,41 +838,53 @@ mod bench {
|
||||
use extra::test::BenchHarness;
|
||||
use rand::*;
|
||||
use mem::size_of;
|
||||
use iter::range;
|
||||
use option::{Some, None};
|
||||
|
||||
static N: u64 = 100;
|
||||
|
||||
#[bench]
|
||||
fn rand_xorshift(bh: &mut BenchHarness) {
|
||||
let mut rng = XorShiftRng::new();
|
||||
do bh.iter {
|
||||
rng.gen::<uint>();
|
||||
for _ in range(0, N) {
|
||||
rng.gen::<uint>();
|
||||
}
|
||||
}
|
||||
bh.bytes = size_of::<uint>() as u64;
|
||||
bh.bytes = size_of::<uint>() as u64 * N;
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn rand_isaac(bh: &mut BenchHarness) {
|
||||
let mut rng = IsaacRng::new();
|
||||
do bh.iter {
|
||||
rng.gen::<uint>();
|
||||
for _ in range(0, N) {
|
||||
rng.gen::<uint>();
|
||||
}
|
||||
}
|
||||
bh.bytes = size_of::<uint>() as u64;
|
||||
bh.bytes = size_of::<uint>() as u64 * N;
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn rand_isaac64(bh: &mut BenchHarness) {
|
||||
let mut rng = Isaac64Rng::new();
|
||||
do bh.iter {
|
||||
rng.gen::<uint>();
|
||||
for _ in range(0, N) {
|
||||
rng.gen::<uint>();
|
||||
}
|
||||
}
|
||||
bh.bytes = size_of::<uint>() as u64;
|
||||
bh.bytes = size_of::<uint>() as u64 * N;
|
||||
}
|
||||
|
||||
#[bench]
|
||||
fn rand_std(bh: &mut BenchHarness) {
|
||||
let mut rng = StdRng::new();
|
||||
do bh.iter {
|
||||
rng.gen::<uint>();
|
||||
for _ in range(0, N) {
|
||||
rng.gen::<uint>();
|
||||
}
|
||||
}
|
||||
bh.bytes = size_of::<uint>() as u64;
|
||||
bh.bytes = size_of::<uint>() as u64 * N;
|
||||
}
|
||||
|
||||
#[bench]
|
||||
|
@ -30,8 +30,12 @@
|
||||
// assume they work when we call them.
|
||||
|
||||
/// A random number generator that retrieves randomness straight from
|
||||
/// the operating system. On Unix-like systems this reads from
|
||||
/// `/dev/urandom`, on Windows this uses `CryptGenRandom`.
|
||||
/// the operating system. Platform sources:
|
||||
///
|
||||
/// - Unix-like systems (Linux, Android, Mac OSX): read directly from
|
||||
/// `/dev/urandom`.
|
||||
/// - Windows: calls `CryptGenRandom`, using the default cryptographic
|
||||
/// service provider with the `PROV_RSA_FULL` type.
|
||||
///
|
||||
/// This does not block.
|
||||
#[cfg(unix)]
|
||||
@ -39,8 +43,12 @@ pub struct OSRng {
|
||||
priv inner: ReaderRng<file::FileStream>
|
||||
}
|
||||
/// A random number generator that retrieves randomness straight from
|
||||
/// the operating system. On Unix-like systems this reads from
|
||||
/// `/dev/urandom`, on Windows this uses `CryptGenRandom`.
|
||||
/// the operating system. Platform sources:
|
||||
///
|
||||
/// - Unix-like systems (Linux, Android, Mac OSX): read directly from
|
||||
/// `/dev/urandom`.
|
||||
/// - Windows: calls `CryptGenRandom`, using the default cryptographic
|
||||
/// service provider with the `PROV_RSA_FULL` type.
|
||||
///
|
||||
/// This does not block.
|
||||
#[cfg(windows)]
|
||||
|
235
src/libstd/rand/range.rs
Normal file
235
src/libstd/rand/range.rs
Normal file
@ -0,0 +1,235 @@
|
||||
// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
|
||||
// file at the top-level directory of this distribution and at
|
||||
// http://rust-lang.org/COPYRIGHT.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
//! Generating numbers between two others.
|
||||
|
||||
// this is surprisingly complicated to be both generic & correct
|
||||
|
||||
use cmp::Ord;
|
||||
use num::Bounded;
|
||||
use rand::Rng;
|
||||
use rand::distributions::{Sample, IndependentSample};
|
||||
|
||||
/// Sample values uniformly between two bounds.
|
||||
///
|
||||
/// This gives a uniform distribution (assuming the RNG used to sample
|
||||
/// it is itself uniform & the `SampleRange` implementation for the
|
||||
/// given type is correct), even for edge cases like `low = 0u8`,
|
||||
/// `high = 170u8`, for which a naive modulo operation would return
|
||||
/// numbers less than 85 with double the probability to those greater
|
||||
/// than 85.
|
||||
///
|
||||
/// Types should attempt to sample in `[low, high)`, i.e., not
|
||||
/// including `high`, but this may be very difficult. All the
|
||||
/// primitive integer types satisfy this property, and the float types
|
||||
/// normally satisfy it, but rounding may mean `high` can occur.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use std::rand;
|
||||
/// use std::rand::distributions::{IndependentSample, Range};
|
||||
///
|
||||
/// fn main() {
|
||||
/// let between = Range::new(10u, 10000u);
|
||||
/// let rng = rand::task_rng();
|
||||
/// let mut sum = 0;
|
||||
/// for _ in range(0, 1000) {
|
||||
/// sum += between.ind_sample(rng);
|
||||
/// }
|
||||
/// println!("{}", sum);
|
||||
/// }
|
||||
/// ```
|
||||
pub struct Range<X> {
|
||||
priv low: X,
|
||||
priv range: X,
|
||||
priv accept_zone: X
|
||||
}
|
||||
|
||||
impl<X: SampleRange + Ord> Range<X> {
|
||||
/// Create a new `Range` instance that samples uniformly from
|
||||
/// `[low, high)`. Fails if `low >= high`.
|
||||
pub fn new(low: X, high: X) -> Range<X> {
|
||||
assert!(low < high, "Range::new called with `low >= high`");
|
||||
SampleRange::construct_range(low, high)
|
||||
}
|
||||
}
|
||||
|
||||
impl<Sup: SampleRange> Sample<Sup> for Range<Sup> {
|
||||
#[inline]
|
||||
fn sample<R: Rng>(&mut self, rng: &mut R) -> Sup { self.ind_sample(rng) }
|
||||
}
|
||||
impl<Sup: SampleRange> IndependentSample<Sup> for Range<Sup> {
|
||||
fn ind_sample<R: Rng>(&self, rng: &mut R) -> Sup {
|
||||
SampleRange::sample_range(self, rng)
|
||||
}
|
||||
}
|
||||
|
||||
/// The helper trait for types that have a sensible way to sample
|
||||
/// uniformly between two values. This should not be used directly,
|
||||
/// and is only to facilitate `Range`.
|
||||
pub trait SampleRange {
|
||||
/// Construct the `Range` object that `sample_range`
|
||||
/// requires. This should not ever be called directly, only via
|
||||
/// `Range::new`, which will check that `low < high`, so this
|
||||
/// function doesn't have to repeat the check.
|
||||
fn construct_range(low: Self, high: Self) -> Range<Self>;
|
||||
|
||||
/// Sample a value from the given `Range` with the given `Rng` as
|
||||
/// a source of randomness.
|
||||
fn sample_range<R: Rng>(r: &Range<Self>, rng: &mut R) -> Self;
|
||||
}
|
||||
|
||||
macro_rules! integer_impl {
|
||||
($ty:ty, $unsigned:ty) => {
|
||||
impl SampleRange for $ty {
|
||||
// we play free and fast with unsigned vs signed here
|
||||
// (when $ty is signed), but that's fine, since the
|
||||
// contract of this macro is for $ty and $unsigned to be
|
||||
// "bit-equal", so casting between them is a no-op & a
|
||||
// bijection.
|
||||
|
||||
fn construct_range(low: $ty, high: $ty) -> Range<$ty> {
|
||||
let range = high as $unsigned - low as $unsigned;
|
||||
let unsigned_max: $unsigned = Bounded::max_value();
|
||||
|
||||
// this is the largest number that fits into $unsigned
|
||||
// that `range` divides evenly, so, if we've sampled
|
||||
// `n` uniformly from this region, then `n % range` is
|
||||
// uniform in [0, range)
|
||||
let zone = unsigned_max - unsigned_max % range;
|
||||
|
||||
Range {
|
||||
low: low,
|
||||
range: range as $ty,
|
||||
accept_zone: zone as $ty
|
||||
}
|
||||
}
|
||||
#[inline]
|
||||
fn sample_range<R: Rng>(r: &Range<$ty>, rng: &mut R) -> $ty {
|
||||
loop {
|
||||
// rejection sample
|
||||
let v = rng.gen::<$unsigned>();
|
||||
// until we find something that fits into the
|
||||
// region which r.range evenly divides (this will
|
||||
// be uniformly distributed)
|
||||
if v < r.accept_zone as $unsigned {
|
||||
// and return it, with some adjustments
|
||||
return r.low + (v % r.range as $unsigned) as $ty;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
integer_impl! { i8, u8 }
|
||||
integer_impl! { i16, u16 }
|
||||
integer_impl! { i32, u32 }
|
||||
integer_impl! { i64, u64 }
|
||||
integer_impl! { int, uint }
|
||||
integer_impl! { u8, u8 }
|
||||
integer_impl! { u16, u16 }
|
||||
integer_impl! { u32, u32 }
|
||||
integer_impl! { u64, u64 }
|
||||
integer_impl! { uint, uint }
|
||||
|
||||
macro_rules! float_impl {
|
||||
($ty:ty) => {
|
||||
impl SampleRange for $ty {
|
||||
fn construct_range(low: $ty, high: $ty) -> Range<$ty> {
|
||||
Range {
|
||||
low: low,
|
||||
range: high - low,
|
||||
accept_zone: 0.0 // unused
|
||||
}
|
||||
}
|
||||
fn sample_range<R: Rng>(r: &Range<$ty>, rng: &mut R) -> $ty {
|
||||
r.low + r.range * rng.gen()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float_impl! { f32 }
|
||||
float_impl! { f64 }
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use rand::*;
|
||||
use num::Bounded;
|
||||
use iter::range;
|
||||
use option::{Some, None};
|
||||
use vec::ImmutableVector;
|
||||
|
||||
#[should_fail]
|
||||
#[test]
|
||||
fn test_range_bad_limits_equal() {
|
||||
Range::new(10, 10);
|
||||
}
|
||||
#[should_fail]
|
||||
#[test]
|
||||
fn test_range_bad_limits_flipped() {
|
||||
Range::new(10, 5);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_integers() {
|
||||
let rng = task_rng();
|
||||
macro_rules! t (
|
||||
($($ty:ty),*) => {{
|
||||
$(
|
||||
let v: &[($ty, $ty)] = [(0, 10),
|
||||
(10, 127),
|
||||
(Bounded::min_value(), Bounded::max_value())];
|
||||
for &(low, high) in v.iter() {
|
||||
let mut sampler: Range<$ty> = Range::new(low, high);
|
||||
for _ in range(0, 1000) {
|
||||
let v = sampler.sample(rng);
|
||||
assert!(low <= v && v < high);
|
||||
let v = sampler.ind_sample(rng);
|
||||
assert!(low <= v && v < high);
|
||||
}
|
||||
}
|
||||
)*
|
||||
}}
|
||||
);
|
||||
t!(i8, i16, i32, i64, int,
|
||||
u8, u16, u32, u64, uint)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_floats() {
|
||||
let rng = task_rng();
|
||||
macro_rules! t (
|
||||
($($ty:ty),*) => {{
|
||||
$(
|
||||
let v: &[($ty, $ty)] = [(0.0, 100.0),
|
||||
(-1e35, -1e25),
|
||||
(1e-35, 1e-25),
|
||||
(-1e35, 1e35)];
|
||||
for &(low, high) in v.iter() {
|
||||
let mut sampler: Range<$ty> = Range::new(low, high);
|
||||
for _ in range(0, 1000) {
|
||||
let v = sampler.sample(rng);
|
||||
assert!(low <= v && v < high);
|
||||
let v = sampler.ind_sample(rng);
|
||||
assert!(low <= v && v < high);
|
||||
}
|
||||
}
|
||||
)*
|
||||
}}
|
||||
);
|
||||
|
||||
t!(f32, f64)
|
||||
}
|
||||
|
||||
}
|
@ -1117,7 +1117,7 @@ fn megapipe_stress() {
|
||||
let total = stress_factor() + 10;
|
||||
let mut rng = rand::rng();
|
||||
do total.times {
|
||||
let msgs = rng.gen_integer_range(0u, 10);
|
||||
let msgs = rng.gen_range(0u, 10);
|
||||
let pipe_clone = pipe.clone();
|
||||
let end_chan_clone = end_chan.clone();
|
||||
do spawntask_random {
|
||||
|
@ -431,7 +431,7 @@ fn find_work(&mut self) -> Option<~Task> {
|
||||
fn try_steals(&mut self) -> Option<~Task> {
|
||||
let work_queues = &mut self.work_queues;
|
||||
let len = work_queues.len();
|
||||
let start_index = self.rng.gen_integer_range(0, len);
|
||||
let start_index = self.rng.gen_range(0, len);
|
||||
for index in range(0, len).map(|i| (i + start_index) % len) {
|
||||
match work_queues[index].steal() {
|
||||
Some(task) => {
|
||||
|
@ -672,6 +672,8 @@ extern "C" CDECL void
|
||||
rust_win32_rand_acquire(HCRYPTPROV* phProv) {
|
||||
win32_require
|
||||
(_T("CryptAcquireContext"),
|
||||
// changes to the parameters here should be reflected in the docs of
|
||||
// std::rand::os::OSRng
|
||||
CryptAcquireContext(phProv, NULL, NULL, PROV_RSA_FULL,
|
||||
CRYPT_VERIFYCONTEXT|CRYPT_SILENT));
|
||||
|
||||
|
@ -90,7 +90,7 @@ fn vec_plus() {
|
||||
let mut v = ~[];
|
||||
let mut i = 0;
|
||||
while i < 1500 {
|
||||
let rv = vec::from_elem(r.gen_integer_range(0u, i + 1), i);
|
||||
let rv = vec::from_elem(r.gen_range(0u, i + 1), i);
|
||||
if r.gen() {
|
||||
v.push_all_move(rv);
|
||||
} else {
|
||||
@ -106,7 +106,7 @@ fn vec_append() {
|
||||
let mut v = ~[];
|
||||
let mut i = 0;
|
||||
while i < 1500 {
|
||||
let rv = vec::from_elem(r.gen_integer_range(0u, i + 1), i);
|
||||
let rv = vec::from_elem(r.gen_range(0u, i + 1), i);
|
||||
if r.gen() {
|
||||
v = vec::append(v, rv);
|
||||
}
|
||||
@ -122,7 +122,7 @@ fn vec_push_all() {
|
||||
|
||||
let mut v = ~[];
|
||||
for i in range(0u, 1500) {
|
||||
let mut rv = vec::from_elem(r.gen_integer_range(0u, i + 1), i);
|
||||
let mut rv = vec::from_elem(r.gen_range(0u, i + 1), i);
|
||||
if r.gen() {
|
||||
v.push_all(rv);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user