【问题标题】:How do I shuffle a VecDeque?如何随机播放 VecDeque?
【发布时间】:2016-12-18 13:15:31
【问题描述】:

我可以像这样简单地打乱一个常规向量:

extern crate rand;
use rand::Rng;

fn shuffle(coll: &mut Vec<i32>) {
    rand::thread_rng().shuffle(coll);
}

问题是,我的代码现在需要使用std::collections::VecDeque,这导致该代码无法编译。

解决这个问题的最简单方法是什么?

【问题讨论】:

    标签: rust shuffle deque


    【解决方案1】:

    不幸的是,rand::Rng::shuffle method 被定义为随机切片。由于其自身的复杂性限制,VecDeque 无法将其元素存储在切片中,因此永远无法在 VecDeque 上直接调用 shuffle

    shuffle 算法的 values 参数的真正要求是有限序列长度、O(1) 元素访问和交换元素的能力,VecDeque 满足所有这些要求。如果有一个包含这些特性的特性会很好,这样values 可以是通用的,但没有。

    使用当前库,您有两种选择:

    • 使用Vec::from(deque)VecDeque 复制到临时Vec,打乱向量,并将内容返回给VecDeque。这种操作的复杂性将保持为 O(n),但它可能需要为临时向量分配大量且成本高昂的堆。

    • 自己在VecDeque 上实现随机播放。 rand::Rng 使用的 Fisher-Yates shuffle 很好理解且易于实现,在其现代形式中,它归结为大约只有 5 lines of code。虽然理论上标准库可以切换到不同的 shuffle 算法,但这在实践中不太可能发生。

    第二个选项的通用形式,使用 trait 来表达 len-and-swap 要求,并采用 rand::Rng::shuffle 的代码,可能如下所示:

    extern crate rand;
    
    use std::collections::VecDeque;
    
    // Real requirement for shuffle
    trait LenAndSwap {
        fn len(&self) -> usize;
        fn swap(&mut self, i: usize, j: usize);
    }
    
    // An exact copy of rand::Rng::shuffle, with the signature modified to
    // accept any type that implements LenAndSwap
    fn shuffle<T, R>(values: &mut T, mut rng: R)
        where T: LenAndSwap,
              R: rand::Rng {
        let mut i = values.len();
        while i >= 2 {
            // invariant: elements with index >= i have been locked in place.
            i -= 1;
            // lock element i in place.
            values.swap(i, rng.gen_range(0, i + 1));
        }
    }
    
    // VecDeque trivially fulfills the LenAndSwap requirement, but
    // we have to spell it out.
    impl<T> LenAndSwap for VecDeque<T> {
        fn len(&self) -> usize {
            self.len()
        }
        fn swap(&mut self, i: usize, j: usize) {
            self.swap(i, j)
        }
    }
    
    fn main() {
        let mut v: VecDeque<u64> = [1, 2, 3, 4].iter().cloned().collect();
        shuffle(&mut v, rand::thread_rng());
        println!("{:?}", v);
    }
    

    【讨论】:

    • 感谢您提供如此冗长而详细的解释!
    • 确实,shuffle 参数是非常不幸的,因为没有使用邻接。
    • @MatthieuM。也许shuffle 不应该在切片上定义,而是在Shufflable 特征(默认为切片定义)上定义与上面的LenAndSwap 相同的方式?这将与当前定义向后兼容。
    【解决方案2】:

    您可以使用make_contiguous (documentation) 创建一个可变切片,然后可以随机播放:

    use rand::prelude::*;
    use std::collections::VecDeque;
    
    fn main() {
        let mut deque = VecDeque::new();
        for p in 0..10 {
            deque.push_back(p);
        }
        deque.make_contiguous().shuffle(&mut rand::thread_rng());
        println!("Random deque: {:?}", deque)
    }
    

    Playground Link如果您想在线试用。

    【讨论】:

      【解决方案3】:

      分别打乱VecDeque的组件,以VecDeque.html::as_mut_slices开头:

      use rand::seq::SliceRandom; // 0.6.5;
      use std::collections::VecDeque; 
      
      fn shuffle(coll: &mut VecDeque<i32>) {
          let mut rng = rand::thread_rng();
          let (a, b) = coll.as_mut_slices();
          a.shuffle(&mut rng);
          b.shuffle(&mut rng);
      }
      

      作为Lukas Kalbertodt points out,此解决方案从不在两个切片之间交换元素,因此不会发生一定程度的随机化。根据您对随机化的需求,这可能不明显或破坏交易。

      【讨论】:

      • 据我了解,这不会产生相同的效果,因为元素永远不会在两个切片之间交换。所以这不是一个完整的洗牌,它不同于“到Vec,洗牌,到VecDeque”的解决方案......
      • @LukasKalbertodt 说得好!我想这取决于用例这种随机化的有效性。
      猜你喜欢
      • 1970-01-01
      • 2023-01-23
      • 1970-01-01
      • 2011-01-27
      • 2020-09-12
      • 1970-01-01
      相关资源
      最近更新 更多