Reputation: 13
I have a specific use case where I need to choose the maximum element from an Iterator<Item = (i8, f64)>
. If there are multiple maximum elements in the Iterator I want to randomly select a maximum element uniformly distributed.
I tried the max_by
function for Iterators but it doesn't work as I need it because it always returns the last element if there a multiple maximum elements.
Here the example:
fn main() {
let v = [(-1i8, 0.4f64), (0, 0.2), (1, 0.4)];
let max = v.into_iter().max_by(|(_, r), (_, s)| r.total_cmp(s)).unwrap(); //always returns (1, 0.4)
println!("{:?}", max);
}
I need a function which returns another Iterator over the maximum elements. Then I could choose a random element from that Iterator.
Upvotes: 1
Views: 1161
Reputation: 23414
You could have your comparison function return Less
or Greater
at random when the compared items are equal, with a suitable weighting to get uniform sampling:
use rand::{ self, Rng };
use std::cmp::Ordering;
let mut rng = rand::thread_rng();
let mut count = 0;
let max = v.into_iter().max_by(move |(_, r), (_, s)| {
match r.total_cmp(s) {
Ordering::Equal => {
count = count + 1;
if rng.gen_range (0..=count) == 0 {
Ordering::Less
} else {
Ordering::Greater
}
},
Ordering::Less => {
count = 0;
Ordering::Less
},
Ordering::Greater => Ordering::Greater,
}
}).unwrap();
Note however that this depends on the implementation of Iter::max_by
(specifically on the order it passes arguments to the comparison closure), so you might need to rewrite it yourself (e.g. using Iter::fold
or Iter::reduce
) to make sure of the order.
Upvotes: 0