@@ -282,10 +282,11 @@ where
282282 }
283283}
284284
285- /// Randomly sample exactly `amount` distinct indices from `0..length`
285+ /// Randomly sample `amount` distinct indices from `0..length`
286286///
287- /// Results are in arbitrary order (there is no guarantee of shuffling or
288- /// ordering).
287+ /// The result may contain less than `amount` indices if insufficient non-zero
288+ /// weights are available. Results are returned in an arbitrary order (there is
289+ /// no guarantee of shuffling or ordering).
289290///
290291/// Function `weight` is called once for each index to provide weights.
291292///
@@ -295,7 +296,6 @@ where
295296///
296297/// Error cases:
297298/// - [`WeightError::InvalidWeight`] when a weight is not-a-number or negative.
298- /// - [`WeightError::InsufficientNonZero`] when fewer than `amount` weights are positive.
299299///
300300/// This implementation uses `O(length + amount)` space and `O(length)` time.
301301#[ cfg( feature = "std" ) ]
@@ -328,18 +328,20 @@ where
328328 }
329329}
330330
331- /// Randomly sample exactly `amount` distinct indices from `0..length`, and
332- /// return them in an arbitrary order (there is no guarantee of shuffling or
333- /// ordering). The weights are to be provided by the input function `weights`,
334- /// which will be called once for each index.
331+ /// Randomly sample `amount` distinct indices from `0..length`
332+ ///
333+ /// The result may contain less than `amount` indices if insufficient non-zero
334+ /// weights are available. Results are returned in an arbitrary order (there is
335+ /// no guarantee of shuffling or ordering).
336+ ///
337+ /// Function `weight` is called once for each index to provide weights.
335338///
336339/// This implementation is based on the algorithm A-ExpJ as found in
337340/// [Efraimidis and Spirakis, 2005](https://doi.org/10.1016/j.ipl.2005.11.003).
338341/// It uses `O(length + amount)` space and `O(length)` time.
339342///
340343/// Error cases:
341344/// - [`WeightError::InvalidWeight`] when a weight is not-a-number or negative.
342- /// - [`WeightError::InsufficientNonZero`] when fewer than `amount` weights are positive.
343345#[ cfg( feature = "std" ) ]
344346fn sample_efraimidis_spirakis < R , F , X , N > (
345347 rng : & mut R ,
@@ -403,28 +405,26 @@ where
403405 index += N :: one ( ) ;
404406 }
405407
406- if candidates. len ( ) < amount. as_usize ( ) {
407- return Err ( WeightError :: InsufficientNonZero ) ;
408- }
408+ if index < length {
409+ let mut x = rng. random :: < f64 > ( ) . ln ( ) / candidates. peek ( ) . unwrap ( ) . key ;
410+ while index < length {
411+ let weight = weight ( index. as_usize ( ) ) . into ( ) ;
412+ if weight > 0.0 {
413+ x -= weight;
414+ if x <= 0.0 {
415+ let min_candidate = candidates. pop ( ) . unwrap ( ) ;
416+ let t = ( min_candidate. key * weight) . exp ( ) ;
417+ let key = rng. random_range ( t..1.0 ) . ln ( ) / weight;
418+ candidates. push ( Element { index, key } ) ;
409419
410- let mut x = rng. random :: < f64 > ( ) . ln ( ) / candidates. peek ( ) . unwrap ( ) . key ;
411- while index < length {
412- let weight = weight ( index. as_usize ( ) ) . into ( ) ;
413- if weight > 0.0 {
414- x -= weight;
415- if x <= 0.0 {
416- let min_candidate = candidates. pop ( ) . unwrap ( ) ;
417- let t = ( min_candidate. key * weight) . exp ( ) ;
418- let key = rng. random_range ( t..1.0 ) . ln ( ) / weight;
419- candidates. push ( Element { index, key } ) ;
420-
421- x = rng. random :: < f64 > ( ) . ln ( ) / candidates. peek ( ) . unwrap ( ) . key ;
420+ x = rng. random :: < f64 > ( ) . ln ( ) / candidates. peek ( ) . unwrap ( ) . key ;
421+ }
422+ } else if !( weight >= 0.0 ) {
423+ return Err ( WeightError :: InvalidWeight ) ;
422424 }
423- } else if !( weight >= 0.0 ) {
424- return Err ( WeightError :: InvalidWeight ) ;
425- }
426425
427- index += N :: one ( ) ;
426+ index += N :: one ( ) ;
427+ }
428428 }
429429
430430 Ok ( IndexVec :: from (
@@ -653,7 +653,7 @@ mod test {
653653 }
654654
655655 let r = sample_weighted ( & mut seed_rng ( 423 ) , 10 , |i| i as f64 , 10 ) ;
656- assert_eq ! ( r. unwrap_err ( ) , WeightError :: InsufficientNonZero ) ;
656+ assert_eq ! ( r. unwrap ( ) . len ( ) , 9 ) ;
657657 }
658658
659659 #[ test]
0 commit comments