【问题标题】:Can I randomly sample from a HashSet efficiently?我可以有效地从 HashSet 中随机抽样吗?
【发布时间】:2018-12-13 04:27:46
【问题描述】:

我有一个std::collections::HashSet,我想采样并删除一个均匀随机的元素。

目前,我正在做的是使用rand.gen_range 对索引进行随机抽样,然后将HashSet 迭代到该索引以获取元素。然后我删除选定的元素。这有效,但效率不高。有没有一种有效的方法来随机抽样一个元素?

这是我的代码的精简版:

use std::collections::HashSet;

extern crate rand;
use rand::thread_rng;
use rand::Rng;

let mut hash_set = HashSet::new();

// ... Fill up hash_set ...

let index = thread_rng().gen_range(0, hash_set.len());
let element = hash_set.iter().nth(index).unwrap().clone();
hash_set.remove(&element);

// ... Use element ...

【问题讨论】:

    标签: random rust hashset


    【解决方案1】:

    考虑 Sven Marnach 的回答,我想使用向量,但我也需要恒定时间插入而不重复。然后我意识到我可以同时维护向量和集合,并确保它们始终具有相同的元素。这将允许使用重复数据删除的恒定时间插入和恒定时间随机删除。

    这是我最终得到的实现:

    struct VecSet<T> {
        set: HashSet<T>,
        vec: Vec<T>,
    }
    
    impl<T> VecSet<T>
    where
        T: Clone + Eq + std::hash::Hash,
    {
        fn new() -> Self {
            Self {
                set: HashSet::new(),
                vec: Vec::new(),
            }
        }
        fn insert(&mut self, elem: T) {
            assert_eq!(self.set.len(), self.vec.len());
            let was_new = self.set.insert(elem.clone());
            if was_new {
                self.vec.push(elem);
            }
        }
        fn remove_random(&mut self) -> T {
            assert_eq!(self.set.len(), self.vec.len());
            let index = thread_rng().gen_range(0, self.vec.len());
            let elem = self.vec.swap_remove(index);
            let was_present = self.set.remove(&elem);
            assert!(was_present);
            elem
        }
        fn is_empty(&self) -> bool {
            assert_eq!(self.set.len(), self.vec.len());
            self.vec.is_empty()
        }
    }
    

    【讨论】:

      【解决方案2】:

      唯一允许在恒定时间内均匀采样的数据结构是具有恒定时间索引访问的数据结构。 HashSet 不提供索引,因此无法在恒定时间内生成随机样本。

      我建议先将您的哈希集转换为Vec,然后从向量中采样。要删除一个元素,只需将最后一个元素移到其位置即可 - 无论如何,向量中元素的顺序无关紧要。

      如果您想以随机顺序消耗集合中的所有元素,您也可以将向量洗牌一次,然后对其进行迭代。

      这是一个在恒定时间内从Vec 中删除随机元素的示例实现:

      use rand::{thread_rng, Rng};
      
      pub trait RemoveRandom {
          type Item;
      
          fn remove_random<R: Rng>(&mut self, rng: &mut R) -> Option<Self::Item>;
      }
      
      impl<T> RemoveRandom for Vec<T> {
          type Item = T;
      
          fn remove_random<R: Rng>(&mut self, rng: &mut R) -> Option<Self::Item> {
              if self.len() == 0 {
                  None
              } else {
                  let index = rng.gen_range(0..self.len());
                  Some(self.swap_remove(index))
              }
          }
      }
      

      (Playground)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-09-24
        • 1970-01-01
        • 1970-01-01
        • 2021-12-17
        相关资源
        最近更新 更多