rand/seq/slice.rs
1// Copyright 2018-2023 Developers of the Rand project.
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9//! `IndexedRandom`, `IndexedMutRandom`, `SliceRandom`
10
11use super::increasing_uniform::IncreasingUniform;
12use super::index;
13#[cfg(feature = "alloc")]
14use crate::distr::uniform::{SampleBorrow, SampleUniform};
15#[cfg(feature = "alloc")]
16use crate::distr::weighted::{Error as WeightError, Weight};
17use crate::{Rng, RngExt};
18use core::ops::{Index, IndexMut};
19
20/// Extension trait on indexable lists, providing random sampling methods.
21///
22/// This trait is implemented on `[T]` slice types. Other types supporting
23/// [`std::ops::Index<usize>`] may implement this (only [`Self::len`] must be
24/// specified).
25pub trait IndexedRandom: Index<usize> {
26 /// The length
27 fn len(&self) -> usize;
28
29 /// True when the length is zero
30 #[inline]
31 fn is_empty(&self) -> bool {
32 self.len() == 0
33 }
34
35 /// Uniformly sample one element
36 ///
37 /// Returns a reference to one uniformly-sampled random element of
38 /// the slice, or `None` if the slice is empty.
39 ///
40 /// For slices, complexity is `O(1)`.
41 ///
42 /// # Example
43 ///
44 /// ```
45 /// use rand::seq::IndexedRandom;
46 ///
47 /// let choices = [1, 2, 4, 8, 16, 32];
48 /// let mut rng = rand::rng();
49 /// println!("{:?}", choices.choose(&mut rng));
50 /// assert_eq!(choices[..0].choose(&mut rng), None);
51 /// ```
52 fn choose<R>(&self, rng: &mut R) -> Option<&Self::Output>
53 where
54 R: Rng + ?Sized,
55 {
56 if self.is_empty() {
57 None
58 } else {
59 Some(&self[rng.random_range(..self.len())])
60 }
61 }
62
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
86 /// Uniformly sample `amount` distinct elements from self
87 ///
88 /// Chooses `amount` elements from the slice at random, without repetition,
89 /// and in random order. The returned iterator is appropriate both for
90 /// collection into a `Vec` and filling an existing buffer (see example).
91 /// If `amount > self.len()`, all available elements are sampled.
92 ///
93 /// In case this API is not sufficiently flexible, use [`index::sample`].
94 ///
95 /// For slices, complexity is the same as [`index::sample`].
96 ///
97 /// # Example
98 /// ```
99 /// use rand::seq::IndexedRandom;
100 ///
101 /// let mut rng = &mut rand::rng();
102 /// let sample = "Hello, audience!".as_bytes();
103 ///
104 /// // collect the results into a vector:
105 /// let v: Vec<u8> = sample.sample(&mut rng, 3).cloned().collect();
106 ///
107 /// // store in a buffer:
108 /// let mut buf = [0u8; 5];
109 /// for (b, slot) in sample.sample(&mut rng, buf.len()).zip(buf.iter_mut()) {
110 /// *slot = *b;
111 /// }
112 /// ```
113 #[cfg(feature = "alloc")]
114 fn sample<R>(&self, rng: &mut R, amount: usize) -> IndexedSamples<'_, Self, Self::Output>
115 where
116 Self::Output: Sized,
117 R: Rng + ?Sized,
118 {
119 let amount = core::cmp::min(amount, self.len());
120 IndexedSamples {
121 slice: self,
122 _phantom: Default::default(),
123 indices: index::sample(rng, self.len(), amount).into_iter(),
124 }
125 }
126
127 /// Uniformly sample a fixed-size array of distinct elements from self
128 ///
129 /// Chooses `N` elements from the slice at random, without repetition,
130 /// and in random order. Returns `None` if (and only if) `N > self.len()`.
131 ///
132 /// For slices, complexity is the same as [`index::sample_array`].
133 ///
134 /// # Example
135 /// ```
136 /// use rand::seq::IndexedRandom;
137 ///
138 /// let mut rng = &mut rand::rng();
139 /// let sample = "Hello, audience!".as_bytes();
140 ///
141 /// let a: [u8; 3] = sample.sample_array(&mut rng).unwrap();
142 /// ```
143 fn sample_array<R, const N: usize>(&self, rng: &mut R) -> Option<[Self::Output; N]>
144 where
145 Self::Output: Clone + Sized,
146 R: Rng + ?Sized,
147 {
148 let indices = index::sample_array(rng, self.len())?;
149 Some(indices.map(|index| self[index].clone()))
150 }
151
152 /// Biased sampling for one element
153 ///
154 /// Returns a reference to one element of the slice, sampled according
155 /// to the provided weights.
156 ///
157 /// The specified function `weight` maps each item `x` to a relative
158 /// likelihood `weight(x)`. The probability of each item being selected is
159 /// therefore `weight(x) / s`, where `s` is the sum of all `weight(x)`.
160 ///
161 /// For slices of length `n`, complexity is `O(n)`.
162 /// For more information about the underlying algorithm,
163 /// see the [`WeightedIndex`] distribution.
164 ///
165 /// See also [`choose_weighted_mut`].
166 ///
167 /// # Example
168 ///
169 /// ```
170 /// use rand::prelude::*;
171 ///
172 /// let choices = [('a', 2), ('b', 1), ('c', 1), ('d', 0)];
173 /// let mut rng = rand::rng();
174 /// // 50% chance to print 'a', 25% chance to print 'b', 25% chance to print 'c',
175 /// // and 'd' will never be printed
176 /// println!("{:?}", choices.choose_weighted(&mut rng, |item| item.1).unwrap().0);
177 /// ```
178 /// [`choose`]: IndexedRandom::choose
179 /// [`choose_weighted_mut`]: IndexedMutRandom::choose_weighted_mut
180 /// [`WeightedIndex`]: crate::distr::weighted::WeightedIndex
181 #[cfg(feature = "alloc")]
182 fn choose_weighted<R, F, B, X>(
183 &self,
184 rng: &mut R,
185 weight: F,
186 ) -> Result<&Self::Output, WeightError>
187 where
188 R: Rng + ?Sized,
189 F: Fn(&Self::Output) -> B,
190 B: SampleBorrow<X>,
191 X: SampleUniform + Weight + PartialOrd<X>,
192 {
193 use crate::distr::weighted::WeightedIndex;
194 let distr = WeightedIndex::new((0..self.len()).map(|idx| weight(&self[idx])))?;
195 Ok(&self[rng.sample(distr)])
196 }
197
198 /// Biased sampling with replacement
199 ///
200 /// Returns an iterator which samples elements from `self` according to the
201 /// given weights with replacement (i.e. elements may be repeated).
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;
217 let distr = WeightedIndex::new((0..self.len()).map(|idx| weight(&self[idx])))?;
218 Ok(rng.sample_iter(distr).map(|i| &self[i]))
219 }
220
221 /// Biased sampling of `amount` distinct elements
222 ///
223 /// Similar to [`sample`], but where the likelihood of each
224 /// element's inclusion in the output may be specified. Zero-weighted
225 /// elements are never returned; the result may therefore contain fewer
226 /// elements than `amount` even when `self.len() >= amount`. The elements
227 /// are returned in an arbitrary, unspecified order.
228 ///
229 /// The specified function `weight` maps each item `x` to a relative
230 /// likelihood `weight(x)`. The probability of each item being selected is
231 /// therefore `weight(x) / s`, where `s` is the sum of all `weight(x)`.
232 ///
233 /// This implementation uses `O(length + amount)` space and `O(length)` time.
234 /// See [`index::sample_weighted`] for details.
235 ///
236 /// # Example
237 ///
238 /// ```
239 /// use rand::prelude::*;
240 ///
241 /// let choices = [('a', 2), ('b', 1), ('c', 1)];
242 /// let mut rng = rand::rng();
243 /// // First Draw * Second Draw = total odds
244 /// // -----------------------
245 /// // (50% * 50%) + (25% * 67%) = 41.7% chance that the output is `['a', 'b']` in some order.
246 /// // (50% * 50%) + (25% * 67%) = 41.7% chance that the output is `['a', 'c']` in some order.
247 /// // (25% * 33%) + (25% * 33%) = 16.6% chance that the output is `['b', 'c']` in some order.
248 /// println!("{:?}", choices.sample_weighted(&mut rng, 2, |item| item.1).unwrap().collect::<Vec<_>>());
249 /// ```
250 /// [`sample`]: IndexedRandom::sample
251 // Note: this is feature-gated on std due to usage of f64::powf.
252 // If necessary, we may use alloc+libm as an alternative (see PR #1089).
253 #[cfg(feature = "std")]
254 fn sample_weighted<R, F, X>(
255 &self,
256 rng: &mut R,
257 amount: usize,
258 weight: F,
259 ) -> Result<IndexedSamples<'_, Self, Self::Output>, WeightError>
260 where
261 Self::Output: Sized,
262 R: Rng + ?Sized,
263 F: Fn(&Self::Output) -> X,
264 X: Into<f64>,
265 {
266 let amount = core::cmp::min(amount, self.len());
267 Ok(IndexedSamples {
268 slice: self,
269 _phantom: Default::default(),
270 indices: index::sample_weighted(
271 rng,
272 self.len(),
273 |idx| weight(&self[idx]).into(),
274 amount,
275 )?
276 .into_iter(),
277 })
278 }
279
280 /// Deprecated: use [`Self::sample`] instead
281 #[cfg(feature = "alloc")]
282 #[deprecated(since = "0.10.0", note = "Renamed to `sample`")]
283 fn choose_multiple<R>(
284 &self,
285 rng: &mut R,
286 amount: usize,
287 ) -> IndexedSamples<'_, Self, Self::Output>
288 where
289 Self::Output: Sized,
290 R: Rng + ?Sized,
291 {
292 self.sample(rng, amount)
293 }
294
295 /// Deprecated: use [`Self::sample_array`] instead
296 #[deprecated(since = "0.10.0", note = "Renamed to `sample_array`")]
297 fn choose_multiple_array<R, const N: usize>(&self, rng: &mut R) -> Option<[Self::Output; N]>
298 where
299 Self::Output: Clone + Sized,
300 R: Rng + ?Sized,
301 {
302 self.sample_array(rng)
303 }
304
305 /// Deprecated: use [`Self::sample_weighted`] instead
306 #[cfg(feature = "std")]
307 #[deprecated(since = "0.10.0", note = "Renamed to `sample_weighted`")]
308 fn choose_multiple_weighted<R, F, X>(
309 &self,
310 rng: &mut R,
311 amount: usize,
312 weight: F,
313 ) -> Result<IndexedSamples<'_, Self, Self::Output>, WeightError>
314 where
315 Self::Output: Sized,
316 R: Rng + ?Sized,
317 F: Fn(&Self::Output) -> X,
318 X: Into<f64>,
319 {
320 self.sample_weighted(rng, amount, weight)
321 }
322}
323
324/// Extension trait on indexable lists, providing random sampling methods.
325///
326/// This trait is implemented automatically for every type implementing
327/// [`IndexedRandom`] and [`std::ops::IndexMut<usize>`].
328pub trait IndexedMutRandom: IndexedRandom + IndexMut<usize> {
329 /// Uniformly sample one element (mut)
330 ///
331 /// Returns a mutable reference to one uniformly-sampled random element of
332 /// the slice, or `None` if the slice is empty.
333 ///
334 /// For slices, complexity is `O(1)`.
335 fn choose_mut<R>(&mut self, rng: &mut R) -> Option<&mut Self::Output>
336 where
337 R: Rng + ?Sized,
338 {
339 if self.is_empty() {
340 None
341 } else {
342 let len = self.len();
343 Some(&mut self[rng.random_range(..len)])
344 }
345 }
346
347 /// Biased sampling for one element (mut)
348 ///
349 /// Returns a mutable reference to one element of the slice, sampled according
350 /// to the provided weights.
351 ///
352 /// The specified function `weight` maps each item `x` to a relative
353 /// likelihood `weight(x)`. The probability of each item being selected is
354 /// therefore `weight(x) / s`, where `s` is the sum of all `weight(x)`.
355 ///
356 /// For slices of length `n`, complexity is `O(n)`.
357 /// For more information about the underlying algorithm,
358 /// see the [`WeightedIndex`] distribution.
359 ///
360 /// See also [`choose_weighted`].
361 ///
362 /// [`choose_mut`]: IndexedMutRandom::choose_mut
363 /// [`choose_weighted`]: IndexedRandom::choose_weighted
364 /// [`WeightedIndex`]: crate::distr::weighted::WeightedIndex
365 #[cfg(feature = "alloc")]
366 fn choose_weighted_mut<R, F, B, X>(
367 &mut self,
368 rng: &mut R,
369 weight: F,
370 ) -> Result<&mut Self::Output, WeightError>
371 where
372 R: Rng + ?Sized,
373 F: Fn(&Self::Output) -> B,
374 B: SampleBorrow<X>,
375 X: SampleUniform + Weight + PartialOrd<X>,
376 {
377 use crate::distr::{Distribution, weighted::WeightedIndex};
378 let distr = WeightedIndex::new((0..self.len()).map(|idx| weight(&self[idx])))?;
379 let index = distr.sample(rng);
380 Ok(&mut self[index])
381 }
382}
383
384/// Extension trait on slices, providing shuffling methods.
385///
386/// This trait is implemented on all `[T]` slice types, providing several
387/// methods for choosing and shuffling elements. You must `use` this trait:
388///
389/// ```
390/// use rand::seq::SliceRandom;
391///
392/// let mut rng = rand::rng();
393/// let mut bytes = "Hello, random!".to_string().into_bytes();
394/// bytes.shuffle(&mut rng);
395/// let str = String::from_utf8(bytes).unwrap();
396/// println!("{}", str);
397/// ```
398/// Example output (non-deterministic):
399/// ```none
400/// l,nmroHado !le
401/// ```
402pub trait SliceRandom: IndexedMutRandom {
403 /// Shuffle a mutable slice in place.
404 ///
405 /// For slices of length `n`, complexity is `O(n)`.
406 /// The resulting permutation is picked uniformly from the set of all possible permutations.
407 ///
408 /// # Example
409 ///
410 /// ```
411 /// use rand::seq::SliceRandom;
412 ///
413 /// let mut rng = rand::rng();
414 /// let mut y = [1, 2, 3, 4, 5];
415 /// println!("Unshuffled: {:?}", y);
416 /// y.shuffle(&mut rng);
417 /// println!("Shuffled: {:?}", y);
418 /// ```
419 fn shuffle<R>(&mut self, rng: &mut R)
420 where
421 R: Rng + ?Sized;
422
423 /// Sample `amount` shuffled elements
424 ///
425 /// Shuffles `amount` random elements into the end of the slice (`n..` where
426 /// `n = self.len() - amount`). The rest of the slice (`..n`) contains the
427 /// remaining elements in a permuted but not fully shuffled order.
428 ///
429 /// Returns a tuple of the sampled elements (`&mut self[n..]`) and the
430 /// remaining elements (`&mut self[..n]`).
431 ///
432 /// This is an efficient method to select `amount` elements at random from
433 /// the slice, provided the slice may be mutated.
434 ///
435 /// For slices, complexity is `O(m)` where `m = amount`.
436 /// If `amount >= self.len()` this is equivalent to [`Self::shuffle`].
437 ///
438 /// # Example
439 ///
440 /// ```
441 /// use rand::seq::SliceRandom;
442 ///
443 /// let mut rng = rand::rng();
444 /// let mut y = [1, 2, 3, 4, 5];
445 /// let (shuffled, rest) = y.partial_shuffle(&mut rng, 3);
446 /// assert_eq!(shuffled.len(), 3);
447 /// assert_eq!(rest.len(), 2);
448 /// let sampled = shuffled.to_vec();
449 /// assert_eq!(&sampled, &y[2..5]);
450 /// ```
451 #[must_use]
452 fn partial_shuffle<R>(
453 &mut self,
454 rng: &mut R,
455 amount: usize,
456 ) -> (&mut [Self::Output], &mut [Self::Output])
457 where
458 Self::Output: Sized,
459 R: Rng + ?Sized;
460}
461
462impl<T> IndexedRandom for [T] {
463 fn len(&self) -> usize {
464 self.len()
465 }
466}
467
468impl<IR: IndexedRandom + IndexMut<usize> + ?Sized> IndexedMutRandom for IR {}
469
470impl<T> SliceRandom for [T] {
471 fn shuffle<R>(&mut self, rng: &mut R)
472 where
473 R: Rng + ?Sized,
474 {
475 if self.len() <= 1 {
476 // There is no need to shuffle an empty or single element slice
477 return;
478 }
479 let _ = self.partial_shuffle(rng, self.len());
480 }
481
482 fn partial_shuffle<R>(&mut self, rng: &mut R, amount: usize) -> (&mut [T], &mut [T])
483 where
484 R: Rng + ?Sized,
485 {
486 let n = self.len().saturating_sub(amount);
487
488 // The algorithm below is based on Durstenfeld's algorithm for the
489 // [Fisher–Yates shuffle](https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm)
490 // for an unbiased permutation.
491 // It ensures that the last `amount` elements of the slice
492 // are randomly selected from the whole slice.
493
494 // `IncreasingUniform::next_index()` is faster than `Rng::random_range`
495 // but only works for 32 bit integers
496 // So we must use the slow method if the slice is longer than that.
497 if self.len() < (u32::MAX as usize) {
498 let mut chooser = IncreasingUniform::new(rng, n as u32);
499 for i in n..self.len() {
500 let index = chooser.next_index();
501 self.swap(i, index);
502 }
503 } else {
504 for i in n..self.len() {
505 let index = rng.random_range(..i + 1);
506 self.swap(i, index);
507 }
508 }
509 let r = self.split_at_mut(n);
510 (r.1, r.0)
511 }
512}
513
514/// An iterator over multiple slice elements.
515///
516/// This struct is created by
517/// [`IndexedRandom::sample`](trait.IndexedRandom.html#tymethod.sample).
518#[cfg(feature = "alloc")]
519#[derive(Debug)]
520pub struct IndexedSamples<'a, S: ?Sized + 'a, T: 'a> {
521 slice: &'a S,
522 _phantom: core::marker::PhantomData<T>,
523 indices: index::IndexVecIntoIter,
524}
525
526#[cfg(feature = "alloc")]
527impl<'a, S: Index<usize, Output = T> + ?Sized + 'a, T: 'a> Iterator for IndexedSamples<'a, S, T> {
528 type Item = &'a T;
529
530 fn next(&mut self) -> Option<Self::Item> {
531 // TODO: investigate using SliceIndex::get_unchecked when stable
532 self.indices.next().map(|i| &self.slice[i])
533 }
534
535 fn size_hint(&self) -> (usize, Option<usize>) {
536 (self.indices.len(), Some(self.indices.len()))
537 }
538}
539
540#[cfg(feature = "alloc")]
541impl<'a, S: Index<usize, Output = T> + ?Sized + 'a, T: 'a> ExactSizeIterator
542 for IndexedSamples<'a, S, T>
543{
544 fn len(&self) -> usize {
545 self.indices.len()
546 }
547}
548
549/// Deprecated: renamed to [`IndexedSamples`]
550#[cfg(feature = "alloc")]
551#[deprecated(since = "0.10.0", note = "Renamed to `IndexedSamples`")]
552pub type SliceChooseIter<'a, S, T> = IndexedSamples<'a, S, T>;
553
554#[cfg(test)]
555mod test {
556 use super::*;
557 #[cfg(feature = "alloc")]
558 use alloc::vec::Vec;
559
560 #[test]
561 fn test_slice_choose() {
562 let mut r = crate::test::rng(107);
563 let chars = [
564 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
565 ];
566 let mut chosen = [0i32; 14];
567 // The below all use a binomial distribution with n=1000, p=1/14.
568 // binocdf(40, 1000, 1/14) ~= 2e-5; 1-binocdf(106, ..) ~= 2e-5
569 for _ in 0..1000 {
570 let picked = *chars.choose(&mut r).unwrap();
571 chosen[(picked as usize) - ('a' as usize)] += 1;
572 }
573 for count in chosen.iter() {
574 assert!(40 < *count && *count < 106);
575 }
576
577 chosen.iter_mut().for_each(|x| *x = 0);
578 for _ in 0..1000 {
579 *chosen.choose_mut(&mut r).unwrap() += 1;
580 }
581 for count in chosen.iter() {
582 assert!(40 < *count && *count < 106);
583 }
584
585 let mut v: [isize; 0] = [];
586 assert_eq!(v.choose(&mut r), None);
587 assert_eq!(v.choose_mut(&mut r), None);
588 }
589
590 #[test]
591 fn value_stability_slice() {
592 let mut r = crate::test::rng(413);
593 let chars = [
594 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
595 ];
596 let mut nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
597
598 assert_eq!(chars.choose(&mut r), Some(&'l'));
599 assert_eq!(nums.choose_mut(&mut r), Some(&mut 3));
600
601 assert_eq!(
602 &chars.sample_array(&mut r),
603 &Some(['f', 'i', 'd', 'b', 'c', 'm', 'j', 'k'])
604 );
605
606 #[cfg(feature = "alloc")]
607 assert_eq!(
608 &chars.sample(&mut r, 8).cloned().collect::<Vec<char>>(),
609 &['h', 'm', 'd', 'b', 'c', 'e', 'n', 'f']
610 );
611
612 #[cfg(feature = "alloc")]
613 assert_eq!(chars.choose_weighted(&mut r, |_| 1), Ok(&'i'));
614 #[cfg(feature = "alloc")]
615 assert_eq!(nums.choose_weighted_mut(&mut r, |_| 1), Ok(&mut 2));
616
617 let mut r = crate::test::rng(414);
618 nums.shuffle(&mut r);
619 assert_eq!(nums, [5, 11, 0, 8, 7, 12, 6, 4, 9, 3, 1, 2, 10]);
620 nums = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
621 let res = nums.partial_shuffle(&mut r, 6);
622 assert_eq!(res.0, &mut [7, 12, 6, 8, 1, 9]);
623 assert_eq!(res.1, &mut [0, 11, 2, 3, 4, 5, 10]);
624 }
625
626 #[test]
627 #[cfg_attr(miri, ignore)] // Miri is too slow
628 fn test_shuffle() {
629 let mut r = crate::test::rng(108);
630 let empty: &mut [isize] = &mut [];
631 empty.shuffle(&mut r);
632 let mut one = [1];
633 one.shuffle(&mut r);
634 let b: &[_] = &[1];
635 assert_eq!(one, b);
636
637 let mut two = [1, 2];
638 two.shuffle(&mut r);
639 assert!(two == [1, 2] || two == [2, 1]);
640
641 fn move_last(slice: &mut [usize], pos: usize) {
642 // use slice[pos..].rotate_left(1); once we can use that
643 let last_val = slice[pos];
644 for i in pos..slice.len() - 1 {
645 slice[i] = slice[i + 1];
646 }
647 *slice.last_mut().unwrap() = last_val;
648 }
649 let mut counts = [0i32; 24];
650 for _ in 0..10000 {
651 let mut arr: [usize; 4] = [0, 1, 2, 3];
652 arr.shuffle(&mut r);
653 let mut permutation = 0usize;
654 let mut pos_value = counts.len();
655 for i in 0..4 {
656 pos_value /= 4 - i;
657 let pos = arr.iter().position(|&x| x == i).unwrap();
658 assert!(pos < (4 - i));
659 permutation += pos * pos_value;
660 move_last(&mut arr, pos);
661 assert_eq!(arr[3], i);
662 }
663 for (i, &a) in arr.iter().enumerate() {
664 assert_eq!(a, i);
665 }
666 counts[permutation] += 1;
667 }
668 for count in counts.iter() {
669 // Binomial(10000, 1/24) with average 416.667
670 // Octave: binocdf(n, 10000, 1/24)
671 // 99.9% chance samples lie within this range:
672 assert!(352 <= *count && *count <= 483, "count: {}", count);
673 }
674 }
675
676 #[test]
677 fn test_partial_shuffle() {
678 let mut r = crate::test::rng(118);
679
680 let mut empty: [u32; 0] = [];
681 let res = empty.partial_shuffle(&mut r, 10);
682 assert_eq!((res.0.len(), res.1.len()), (0, 0));
683
684 let mut v = [1, 2, 3, 4, 5];
685 let res = v.partial_shuffle(&mut r, 2);
686 assert_eq!((res.0.len(), res.1.len()), (2, 3));
687 assert!(res.0[0] != res.0[1]);
688 // First elements are only modified if selected, so at least one isn't modified:
689 assert!(res.1[0] == 1 || res.1[1] == 2 || res.1[2] == 3);
690 }
691
692 #[test]
693 #[cfg(feature = "alloc")]
694 #[cfg_attr(miri, ignore)] // Miri is too slow
695 fn test_weighted() {
696 let mut r = crate::test::rng(406);
697 const N_REPS: u32 = 3000;
698 let weights = [1u32, 2, 3, 0, 5, 6, 7, 1, 2, 3, 4, 5, 6, 7];
699 let total_weight = weights.iter().sum::<u32>() as f32;
700
701 let verify = |result: [i32; 14]| {
702 for (i, count) in result.iter().enumerate() {
703 let exp = (weights[i] * N_REPS) as f32 / total_weight;
704 let mut err = (*count as f32 - exp).abs();
705 if err != 0.0 {
706 err /= exp;
707 }
708 assert!(err <= 0.25);
709 }
710 };
711
712 // choose_weighted
713 fn get_weight<T>(item: &(u32, T)) -> u32 {
714 item.0
715 }
716 let mut chosen = [0i32; 14];
717 let mut items = [(0u32, 0usize); 14]; // (weight, index)
718 for (i, item) in items.iter_mut().enumerate() {
719 *item = (weights[i], i);
720 }
721 for _ in 0..N_REPS {
722 let item = items.choose_weighted(&mut r, get_weight).unwrap();
723 chosen[item.1] += 1;
724 }
725 verify(chosen);
726
727 // choose_weighted_mut
728 let mut items = [(0u32, 0i32); 14]; // (weight, count)
729 for (i, item) in items.iter_mut().enumerate() {
730 *item = (weights[i], 0);
731 }
732 for _ in 0..N_REPS {
733 items.choose_weighted_mut(&mut r, get_weight).unwrap().1 += 1;
734 }
735 for (ch, item) in chosen.iter_mut().zip(items.iter()) {
736 *ch = item.1;
737 }
738 verify(chosen);
739
740 // Check error cases
741 let empty_slice = &mut [10][0..0];
742 assert_eq!(
743 empty_slice.choose_weighted(&mut r, |_| 1),
744 Err(WeightError::InvalidInput)
745 );
746 assert_eq!(
747 empty_slice.choose_weighted_mut(&mut r, |_| 1),
748 Err(WeightError::InvalidInput)
749 );
750 assert_eq!(
751 ['x'].choose_weighted_mut(&mut r, |_| 0),
752 Err(WeightError::InsufficientNonZero)
753 );
754 assert_eq!(
755 [0, -1].choose_weighted_mut(&mut r, |x| *x),
756 Err(WeightError::InvalidWeight)
757 );
758 assert_eq!(
759 [-1, 0].choose_weighted_mut(&mut r, |x| *x),
760 Err(WeightError::InvalidWeight)
761 );
762 }
763
764 #[test]
765 #[cfg(feature = "std")]
766 fn test_multiple_weighted_edge_cases() {
767 use super::*;
768
769 let mut rng = crate::test::rng(413);
770
771 // Case 1: One of the weights is 0
772 let choices = [('a', 2), ('b', 1), ('c', 0)];
773 for _ in 0..100 {
774 let result = choices
775 .sample_weighted(&mut rng, 2, |item| item.1)
776 .unwrap()
777 .collect::<Vec<_>>();
778
779 assert_eq!(result.len(), 2);
780 assert!(!result.iter().any(|val| val.0 == 'c'));
781 }
782
783 // Case 2: All of the weights are 0
784 let choices = [('a', 0), ('b', 0), ('c', 0)];
785 let r = choices.sample_weighted(&mut rng, 2, |item| item.1);
786 assert_eq!(r.unwrap().len(), 0);
787
788 // Case 3: Negative weights
789 let choices = [('a', -1), ('b', 1), ('c', 1)];
790 let r = choices.sample_weighted(&mut rng, 2, |item| item.1);
791 assert_eq!(r.unwrap_err(), WeightError::InvalidWeight);
792
793 // Case 4: Empty list
794 let choices = [];
795 let r = choices.sample_weighted(&mut rng, 0, |_: &()| 0);
796 assert_eq!(r.unwrap().count(), 0);
797
798 // Case 5: NaN weights
799 let choices = [('a', f64::NAN), ('b', 1.0), ('c', 1.0)];
800 let r = choices.sample_weighted(&mut rng, 2, |item| item.1);
801 assert_eq!(r.unwrap_err(), WeightError::InvalidWeight);
802
803 // Case 6: +infinity weights
804 let choices = [('a', f64::INFINITY), ('b', 1.0), ('c', 1.0)];
805 for _ in 0..100 {
806 let result = choices
807 .sample_weighted(&mut rng, 2, |item| item.1)
808 .unwrap()
809 .collect::<Vec<_>>();
810 assert_eq!(result.len(), 2);
811 assert!(result.iter().any(|val| val.0 == 'a'));
812 }
813
814 // Case 7: -infinity weights
815 let choices = [('a', f64::NEG_INFINITY), ('b', 1.0), ('c', 1.0)];
816 let r = choices.sample_weighted(&mut rng, 2, |item| item.1);
817 assert_eq!(r.unwrap_err(), WeightError::InvalidWeight);
818
819 // Case 8: -0 weights
820 let choices = [('a', -0.0), ('b', 1.0), ('c', 1.0)];
821 let r = choices.sample_weighted(&mut rng, 2, |item| item.1);
822 assert!(r.is_ok());
823 }
824
825 #[test]
826 #[cfg(feature = "std")]
827 #[cfg_attr(miri, ignore)] // Miri is too slow
828 fn test_multiple_weighted_distributions() {
829 use super::*;
830
831 // The theoretical probabilities of the different outcomes are:
832 // AB: 0.5 * 0.667 = 0.3333
833 // AC: 0.5 * 0.333 = 0.1667
834 // BA: 0.333 * 0.75 = 0.25
835 // BC: 0.333 * 0.25 = 0.0833
836 // CA: 0.167 * 0.6 = 0.1
837 // CB: 0.167 * 0.4 = 0.0667
838 let choices = [('a', 3), ('b', 2), ('c', 1)];
839 let mut rng = crate::test::rng(414);
840
841 let mut results = [0i32; 3];
842 let expected_results = [5833, 2667, 1500];
843 for _ in 0..10000 {
844 let result = choices
845 .sample_weighted(&mut rng, 2, |item| item.1)
846 .unwrap()
847 .collect::<Vec<_>>();
848
849 assert_eq!(result.len(), 2);
850
851 match (result[0].0, result[1].0) {
852 ('a', 'b') | ('b', 'a') => {
853 results[0] += 1;
854 }
855 ('a', 'c') | ('c', 'a') => {
856 results[1] += 1;
857 }
858 ('b', 'c') | ('c', 'b') => {
859 results[2] += 1;
860 }
861 (_, _) => panic!("unexpected result"),
862 }
863 }
864
865 let mut diffs = results
866 .iter()
867 .zip(&expected_results)
868 .map(|(a, b)| (a - b).abs());
869 assert!(!diffs.any(|deviation| deviation > 100));
870 }
871}