有没有一种方法可以以相同顺序随机排列两个或多个列表?

时间:2020-03-01 14:59:44

标签: rust

我想要类似这样的伪代码:

a = [1, 2, 3, 4];
b = [3, 4, 5, 6];
iter = a.iter_mut().zip(b.iter_mut());
shuffle(iter);
// example shuffle:
// a = [2, 4, 3, 1];
// b = [4, 6, 5, 3];

更具体地说,是否有一些功能类似于:

fn shuffle<T>(iterator: IterMut<T>) { /* ... */ }

我的具体情况是尝试按行和向量Array2改写(array2:Lndarray:Array2<f32>, vec:Vec<usize>)

特别是array2.iter_axis(Axis(1)).zip(vec.iter())

2 个答案:

答案 0 :(得分:0)

无法就地改组通用迭代器。

但是,对切片执行改组很容易:

use rand::Rng;

pub fn shufflex<T: Copy>(slice: &mut [T]) {
    let mut rng = rand::thread_rng();

    let len = slice.len();
    for i in 0..len {
        let next = rng.gen_range(i, len);
        let tmp = slice[i];
        slice[i] = slice[next];
        slice[next] = tmp;
    }
}

但是也可以编写更通用的shuffle函数,该函数可用于多种类型:

use std::ops::{Index, IndexMut};
use rand::Rng;

pub fn shuffle<T>(indexable: &mut T)
where
    T: IndexMut<usize> + Len + ?Sized,
    T::Output: Copy,
{
    let mut rng = rand::thread_rng();

    let len = indexable.len();
    for i in 0..len {
        let next = rng.gen_range(i, len);
        let tmp = indexable[i];
        indexable[i] = indexable[next];
        indexable[next] = tmp;
    }
}

我写了一个完整的示例,该示例还允许对playground中的多个切片进行改组。

编辑:我想我误会了你想做什么。要以相同方式随机播放多个切片,我将执行this

use rand::Rng;

pub fn shuffle<T: Copy>(slices: &mut [&mut [T]]) {
    if slices.len() > 0 {
        let mut rng = rand::thread_rng();

        let len = slices[0].len();
        assert!(slices.iter().all(|s| s.len() == len));

        for i in 0..len {
            let next = rng.gen_range(i, len);

            for slice in slices.iter_mut() {
                let tmp: T = slice[i];
                slice[i] = slice[next];
                slice[next] = tmp;
            }
        }
    }
}

答案 1 :(得分:0)

要以相同顺序随机播放,您可以首先记住该顺序,然后在每次随机播放中都重复使用它。从rand crate的Fisher-Yates洗牌开始:

fn shuffle<R>(&mut self, rng: &mut R)
where R: Rng + ?Sized {
    for i in (1..self.len()).rev() {
        self.swap(i, gen_index(rng, i + 1));
    }
}

事实证明,我们需要为1和切片长度之间的每个i + 1存储0和i之间的随机数,其顺序相反:

// create a vector of indices for shuffling slices of given length
let indices: Vec<usize> = {
    let mut rng = rand::thread_rng();
    (1..slice_len).rev()
    .map(|i| rng.gen_range(0, i + 1))
    .collect()
};

然后,我们可以实现shuffle的变体,其中我们从上面的随机索引列表中提取它们,而不是生成新的随机数:

// shuffle SLICE taking indices from the provided vector
for (i, &rnd_ind) in (1..slice.len()).rev().zip(&indices) {
    slice.swap(i, rnd_ind);
}

将两者放在一起,您可以使用类似(playground)的方法以相同顺序随机播放多个切片:

pub fn shuffle<T>(slices: &mut [&mut [T]]) {
    if slices.len() == 0 {
        return;
    }
    let indices: Vec<usize> = {
        let mut rng = rand::thread_rng();
        (1..slices[0].len())
            .rev()
            .map(|i| rng.gen_range(0, i + 1))
            .collect()
    };
    for slice in slices {
        assert_eq!(slice.len(), indices.len() + 1);
        for (i, &rnd_ind) in (1..slice.len()).rev().zip(&indices) {
            slice.swap(i, rnd_ind);
        }
    }
}