-
Notifications
You must be signed in to change notification settings - Fork 430
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
sample
with random ordering
#169
Comments
Merely exiting the Fisher–Yates shuffle early will suffice for this |
sample
without stable orderingsample
with random ordering
I threw this together: /// Randomly sample up to `amount` elements from a vector.
/// The order of elements in the sample is random.
///
/// This applies Durstenfeld's algorithm for the [Fisher–Yates shuffle](https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle#The_modern_algorithm)
/// which produces an unbiased permutation.
///
/// # Example
///
/// ```rust
/// use rand::{thread_rng, Rng};
///
/// let mut rng = thread_rng();
/// let mut y = [1, 2, 3];
/// rng.shuffle_unstable(&mut y);
/// println!("{:?}", y);
/// rng.shuffle_unstable(&mut y);
/// println!("{:?}", y);
/// ```
fn sample_unstable<T: Clone>(&mut self, mut values: Vec<T>, amount: usize) -> Vec<T>
where Self: Sized
{
let len = values.len();
let mut i = len;
while i > len-amount {
// invariant: elements with index > i have been locked in place.
i -= 1;
// lock element i in place.
values.swap(i, self.gen_range(0, i + 1));
}
values.split_off(i)
} It should run in O(amount). Note that when |
@TheIronBorn why not use |
I'm not sure this should mutate the input list, as Thanks |
IMO it's better to mutate the list and remove the elements than it is to just drop the entire list and only return the samples |
Something more like this? // returns the sampled items, leaving the others in `values`
fn sample_unstable<T: Clone>(&mut self, values: &mut Vec<T>, amount: usize) -> Vec<T>
where Self: Sized
{
let len = values.len();
let mut i = len;
while i > len-amount {
// invariant: elements with index > i have been locked in place.
i -= 1;
// lock element i in place.
values.swap(i, self.gen_range(0, i + 1));
}
values.split_off(i)
} |
And the fn sample_unstable<T>(&mut self, values: &mut [T], amount: usize) -> &[T]
where Self: Sized
{
let len = values.len();
let mut i = len;
if amount > len/2 {
while i > amount {
// invariant: elements with index > i have been locked in place.
i -= 1;
// lock element i in place.
values.swap(i, self.gen_range(0, i + 1));
}
values.split_at(i).0
} else {
while i > len-amount {
// invariant: elements with index > i have been locked in place.
i -= 1;
// lock element i in place.
values.swap(i, self.gen_range(0, i + 1));
}
values.split_at(i).1
}
} It reduces the worst case time complexity to O(n/2). (also this uses slices rather than vectors, unsure which would be preferred) |
I don't know how the |
I just used a |
Perhaps it would be sufficient to merely add to the documentation, pointing users to further reading. |
I missed that you're just exiting the Fisher–Yates shuffle early when I posted that. I'd suggest first using
|
Oh yeah that's far better |
Still unsure whether this should become a pull request. @burdges's code alleviates the "perfectly random" concern, but this is perhaps too close to |
I named it
|
The new |
Does that mean this issue is solved? |
Yeah, that looks great! I would note that |
I was using
rand::sample
to shuffle an arbitrary number of elements from a list, until I noticed that the ordering was stable, and so no shuffling was happening, only random selection.Is there reason to add such functionality to the crate? Or is the expected way of doing this shuffling, then slicing? Or sampling, then shuffling?
rand: 0.3.16
rustc: 1.21.0-nightly
cargo: 0.22.0-nightly
The text was updated successfully, but these errors were encountered: