Make a function that changes pairs of objects in 3d array

I have function for mutation, which takes 3d Vec of Vecs and "mutates" every pair in each of the sequential pairs (using chunks(2) for this) given some probability.
children is a 3d matrix of shape (n_pop, n_teams, team_len) of Player structure.
mut_prob is some real value from [0, 1] range.

pub struct Player {
    sex: String,
    age: usize,
    city: String,
}

fn mutation(&self, children: &[Vec<Vec<Player>>]) -> Vec<Vec<Vec<Player>>> {
        let range = Uniform::from(0. ..1.);
        // let mutation_type = "replace"; // make split and combine later
        for current_set_teams in children {
            let range_over_indices = Uniform::from(1..team_len - 1);
            let probs = rand::thread_rng().sample_iter(&range).take(n_teams);

            current_set_teams
                .chunks(2)
                .zip(probs)
                .map(|(pair, prob)| {
                    let change_ptr = rand::thread_rng().sample(range_over_indices);
                    if prob > mut_prob {
                        pair
                    } else {
                        pair
                    }
                })
                .collect()
        }

So what this function is doing is calculates probs for each pair in the 2d matrix (so there will be n_teams / 2 (n_teams is usize) elements in probs.
Then it tries to swap some of the elements for this pair of Vec<Player>. How can I do it correctly (if there is even the possibility to make it)?
Probably I could use some code like:

for team_set in children {
        let temp_vec: Vec<Vec<Player>> = Vec::new();
        for ind in (0..n_teams).step_by(2) {
            let mut temp_child1 = team_set[ind].clone(); //
            let mut temp_child2 = team_set[ind + 1].clone();
            let prob_vec: Vec<f32> = rand::thread_rng()
                .sample_iter(&range_probs)
                .take(n_teams / 2)
                .collect();
            if prob_vec[ind / 2] > mut_prob {
                let num_to_swap = rand::thread_rng().sample(range_number_to_swap);
                let indices_to_swap =
                    (0..self.team_len).choose_multiple(&mut rand::thread_rng(), num_to_swap);
                for swap in indices_to_swap {
                    temp_child1[swap] = temp_child2[swap];
                    temp_child2[swap] = team_set[ind][swap];
                }
            }
            temp_vec.push(temp_child1);
            temp_vec.push(temp_child2);
        }
        successors.push(temp_vec);
    }

But there are a lot of allocations so probably it is less sufficient.
How can I do it correctly?

So if I understand you want to convert one 3d structure into another by sometimes swapping adjacent members of the deepest nested Vecs? It's going to involve a lot of allocations no matter what because that's how your data structure is designed.

Here's one way.

If you want to reduce allocations and increase cache locality, you could flatten your 3d structure to a 1d Vec.

An alternative to both of these is to first clone the input, and then iterate over chunks_exact_mut, and perform a swap when you want to.

1 Like

Yes, exactly. Thanks for response.

I'm thinking though to write some custom newtype for the nested vecs for conviniency. But I think I need to allocate a lot since I use a lot of Vec's.
Don't know if I could quit struct of Vec's. Cause if I flatten it to 1d Vec, there is where problems with readability begin.