Skip to content

Commit e310713

Browse files
committed
Optimized path of sample_efraimidis_spirakis is stable
This no longer requires nightly
1 parent 8b92c88 commit e310713

File tree

3 files changed

+26
-60
lines changed

3 files changed

+26
-60
lines changed

Cargo.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ features = ["small_rng", "serde1"]
2828
[features]
2929
# Meta-features:
3030
default = ["std", "std_rng"]
31-
nightly = [] # enables performance optimizations requiring nightly rust
31+
nightly = [] # some additions requiring nightly Rust
3232
serde1 = ["serde", "rand_core/serde1"]
3333

3434
# Option (enabled by default): without "std" rand uses libcore; this option
@@ -41,7 +41,7 @@ alloc = ["rand_core/alloc"]
4141
# Option: use getrandom package for seeding
4242
getrandom = ["rand_core/getrandom"]
4343

44-
# Option (requires nightly): experimental SIMD support
44+
# Option (requires nightly rust): experimental SIMD support
4545
simd_support = ["packed_simd"]
4646

4747
# Option (enabled by default): enable StdRng

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ Optionally, the following dependencies can be enabled:
125125
Additionally, these features configure Rand:
126126

127127
- `small_rng` enables inclusion of the `SmallRng` PRNG
128-
- `nightly` enables some optimizations requiring nightly Rust
128+
- `nightly` includes some additions requiring nightly Rust
129129
- `simd_support` (experimental) enables sampling of SIMD values
130130
(uniformly random SIMD integers and floats), requiring nightly Rust
131131

src/seq/index.rs

+23-57
Original file line numberDiff line numberDiff line change
@@ -267,9 +267,7 @@ where R: Rng + ?Sized {
267267
/// sometimes be useful to have the indices themselves so this is provided as
268268
/// an alternative.
269269
///
270-
/// This implementation uses `O(length + amount)` space and `O(length)` time
271-
/// if the "nightly" feature is enabled, or `O(length)` space and
272-
/// `O(length + amount * log length)` time otherwise.
270+
/// This implementation uses `O(length + amount)` space and `O(length)` time.
273271
///
274272
/// Panics if `amount > length`.
275273
#[cfg(feature = "std")]
@@ -300,9 +298,7 @@ where
300298
///
301299
/// This implementation uses the algorithm described by Efraimidis and Spirakis
302300
/// in this paper: https://doi.org/10.1016/j.ipl.2005.11.003
303-
/// It uses `O(length + amount)` space and `O(length)` time if the
304-
/// "nightly" feature is enabled, or `O(length)` space and `O(length
305-
/// + amount * log length)` time otherwise.
301+
/// It uses `O(length + amount)` space and `O(length)` time.
306302
///
307303
/// Panics if `amount > length`.
308304
#[cfg(feature = "std")]
@@ -347,63 +343,33 @@ where
347343
}
348344
impl<N> Eq for Element<N> {}
349345

350-
#[cfg(feature = "nightly")]
351-
{
352-
let mut candidates = Vec::with_capacity(length.as_usize());
353-
let mut index = N::zero();
354-
while index < length {
355-
let weight = weight(index.as_usize()).into();
356-
if !(weight >= 0.) {
357-
return Err(WeightedError::InvalidWeight);
358-
}
359-
360-
let key = rng.gen::<f64>().powf(1.0 / weight);
361-
candidates.push(Element { index, key });
362-
363-
index += N::one();
346+
let mut candidates = Vec::with_capacity(length.as_usize());
347+
let mut index = N::zero();
348+
while index < length {
349+
let weight = weight(index.as_usize()).into();
350+
if !(weight >= 0.) {
351+
return Err(WeightedError::InvalidWeight);
364352
}
365353

366-
// Partially sort the array to find the `amount` elements with the greatest
367-
// keys. Do this by using `select_nth_unstable` to put the elements with
368-
// the *smallest* keys at the beginning of the list in `O(n)` time, which
369-
// provides equivalent information about the elements with the *greatest* keys.
370-
let (_, mid, greater)
371-
= candidates.select_nth_unstable(length.as_usize() - amount.as_usize());
372-
373-
let mut result: Vec<N> = Vec::with_capacity(amount.as_usize());
374-
result.push(mid.index);
375-
for element in greater {
376-
result.push(element.index);
377-
}
378-
Ok(IndexVec::from(result))
379-
}
380-
381-
#[cfg(not(feature = "nightly"))]
382-
{
383-
use alloc::collections::BinaryHeap;
354+
let key = rng.gen::<f64>().powf(1.0 / weight);
355+
candidates.push(Element { index, key });
384356

385-
// Partially sort the array such that the `amount` elements with the largest
386-
// keys are first using a binary max heap.
387-
let mut candidates = BinaryHeap::with_capacity(length.as_usize());
388-
let mut index = N::zero();
389-
while index < length {
390-
let weight = weight(index.as_usize()).into();
391-
if !(weight >= 0.) {
392-
return Err(WeightedError::InvalidWeight);
393-
}
357+
index += N::one();
358+
}
394359

395-
let key = rng.gen::<f64>().powf(1.0 / weight);
396-
candidates.push(Element { index, key });
360+
// Partially sort the array to find the `amount` elements with the greatest
361+
// keys. Do this by using `select_nth_unstable` to put the elements with
362+
// the *smallest* keys at the beginning of the list in `O(n)` time, which
363+
// provides equivalent information about the elements with the *greatest* keys.
364+
let (_, mid, greater)
365+
= candidates.select_nth_unstable(length.as_usize() - amount.as_usize());
397366

398-
index += N::one();
399-
}
400-
401-
let mut result: Vec<N> = Vec::with_capacity(amount.as_usize());
402-
while result.len() < amount.as_usize() {
403-
result.push(candidates.pop().unwrap().index);
404-
}
405-
Ok(IndexVec::from(result))
367+
let mut result: Vec<N> = Vec::with_capacity(amount.as_usize());
368+
result.push(mid.index);
369+
for element in greater {
370+
result.push(element.index);
406371
}
372+
Ok(IndexVec::from(result))
407373
}
408374

409375
/// Randomly sample exactly `amount` indices from `0..length`, using Floyd's

0 commit comments

Comments
 (0)