Skip to content

Commit 7e34a38

Browse files
committed
Add fns IndexedRandom::choose_iter, choose_weighted_iter
1 parent 88b460e commit 7e34a38

File tree

1 file changed

+50
-3
lines changed

1 file changed

+50
-3
lines changed

src/seq/slice.rs

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,29 @@ pub trait IndexedRandom: Index<usize> {
6060
}
6161
}
6262

63+
/// Return an iterator which samples from `self` with replacement
64+
///
65+
/// Returns `None` if and only if `self.is_empty()`.
66+
///
67+
/// # Example
68+
///
69+
/// ```
70+
/// use rand::seq::IndexedRandom;
71+
///
72+
/// let choices = [1, 2, 4, 8, 16, 32];
73+
/// let mut rng = rand::rng();
74+
/// for choice in choices.choose_iter(&mut rng).unwrap().take(3) {
75+
/// println!("{:?}", choice);
76+
/// }
77+
/// ```
78+
fn choose_iter<R>(&self, rng: &mut R) -> Option<impl Iterator<Item = &Self::Output>>
79+
where
80+
R: Rng + ?Sized,
81+
{
82+
let distr = crate::distr::Uniform::new(0, self.len()).ok()?;
83+
Some(rng.sample_iter(distr).map(|i| &self[i]))
84+
}
85+
6386
/// Uniformly sample `amount` distinct elements from self
6487
///
6588
/// Chooses `amount` elements from the slice at random, without repetition,
@@ -128,7 +151,7 @@ pub trait IndexedRandom: Index<usize> {
128151
/// Biased sampling for one element
129152
///
130153
/// Returns a reference to one element of the slice, sampled according
131-
/// to the provided weights. Returns `None` only if the slice is empty.
154+
/// to the provided weights. Returns `None` if and only if `self.is_empty()`.
132155
///
133156
/// The specified function `weight` maps each item `x` to a relative
134157
/// likelihood `weight(x)`. The probability of each item being selected is
@@ -166,9 +189,33 @@ pub trait IndexedRandom: Index<usize> {
166189
B: SampleBorrow<X>,
167190
X: SampleUniform + Weight + PartialOrd<X>,
168191
{
169-
use crate::distr::{weighted::WeightedIndex, Distribution};
192+
use crate::distr::weighted::WeightedIndex;
193+
let distr = WeightedIndex::new((0..self.len()).map(|idx| weight(&self[idx])))?;
194+
Ok(&self[rng.sample(distr)])
195+
}
196+
197+
/// Biased sampling with replacement
198+
///
199+
/// Returns an iterator which samples elements from `self` according to the
200+
/// given weights with replacement (i.e. elements may be repeated).
201+
/// Returns `None` if and only if `self.is_empty()`.
202+
///
203+
/// See also doc for [`Self::choose_weighted`].
204+
#[cfg(feature = "alloc")]
205+
fn choose_weighted_iter<R, F, B, X>(
206+
&self,
207+
rng: &mut R,
208+
weight: F,
209+
) -> Result<impl Iterator<Item = &Self::Output>, WeightError>
210+
where
211+
R: Rng + ?Sized,
212+
F: Fn(&Self::Output) -> B,
213+
B: SampleBorrow<X>,
214+
X: SampleUniform + Weight + PartialOrd<X>,
215+
{
216+
use crate::distr::weighted::WeightedIndex;
170217
let distr = WeightedIndex::new((0..self.len()).map(|idx| weight(&self[idx])))?;
171-
Ok(&self[distr.sample(rng)])
218+
Ok(rng.sample_iter(distr).map(|i| &self[i]))
172219
}
173220

174221
/// Biased sampling of `amount` distinct elements

0 commit comments

Comments
 (0)