-
-
Notifications
You must be signed in to change notification settings - Fork 480
Description
Background
What is your motivation?
Provide a way to produce predictable results for (a function similar to) rand::seq::IteratorRandom::choose no matter what type of iterator it is.
Right now the behaviour of rand::seq::IteratorRandom::choose depends on the type of Iterator that is passed in. This is mentioned in the docs as an "optimization" however it isn't clear that this is a behaviour affecting "optimization". Furthermore there is no good alternative function which doesn't have this "optimization".
extern crate rand; // 0.7.3
extern crate rand_pcg; // 0.2.1
fn choose(i: &mut dyn Iterator<Item=u32>) -> u32 {
let mut rng = rand_pcg::Pcg32::new(0xcafef00dd15ea5e5, 0xa02bdbf7bb3c0a7);
rand::seq::IteratorRandom::choose(i, &mut rng).unwrap()
}
fn main() {
dbg!(choose(&mut (0..32))); // 5
dbg!(choose(&mut (0..32).filter(|_| true))); // 3
}What type of application is this? numerical simulation
Feature request
- Clarify the documentation to emphasize that
rand::seq::IteratorRandom::choosehas different behaviour depending on the return value of thesize_hintmethod. - Provide an alternative function that will select the same element (including
Rngstate) for any iterator of the same length.
Workarounds
Unify type:
One option to get a consistent result is ensure that you are always working with the same type. For example the following always collects to a Vec.
fn stable_choose_collect<T>(i: impl Iterator<Item=T>, mut rng: impl rand::Rng) -> Option<T> {
rand::seq::IteratorRandom::choose(i.collect::<Vec<_>>().into_iter(), &mut rng)
}Get rid of size_hint.
It would be more "proper" to make a wrapper type but since rand::seq::IteratorRandom::choose only performs its "optimization" if lower == upper we just need to make those not equal. The easiest way to do this is is call .filter(|_| true) on the iterator. Because it can't tell how many elements you will filter it will set the lower bound to 0.
fn stable_choose_no_hint<T>(i: impl Iterator<Item=T>, mut rng: impl rand::Rng) -> Option<T> {
rand::seq::IteratorRandom::choose(i.filter(|_| true), &mut rng)
}