0

I need to take a matrix:

m = [[1, 2],
     [3, 4]]

and shuffle it, which I have been doing like this:

m[0][0], m[0][1], m[1][0], m[1][1] = m[1][0], m[0][0], m[1][1], m[0][1] # or some other combination 

I don't want it to be random because I want to be able to reverse it, and while I don't mind doing the above with a 2x2 matrix I'll need to do it with 4x4's and bigger and don't feel like hard coding all those indexes.

I also don't want to just switch rows, like this: m[0], m[1] = m[1], m[0] but the individual values, m[0][1] and m[1][0] for example.

Merp
  • 118
  • 1
  • 10

2 Answers2

2

You can shuffle the "flattened" indices. By "flattened" in case of 2x2 matrix I mean index 0 is a [0][0], 1 is [0][1], 2 is [1][0], and 3 is [1][1]. In general for the matrix with c columns, index n is equivalent to [n//c][n%c].

The shuffled indices (numbers from 0 to #rows * #columns) use as indices for the new matrix. I.e. replace_indices = [3, 1, 0, 2] would mean that in shuffled matrix 0th index ([0][0]) will be taken from the 3rd index ([1][1]) of the original matrix. And so on:

from random import shuffle

m = ...

rows = len(m)
cols = len(m[0])

replace_indices = list(range(rows * cols))
shuffle(replace_indices)

shuffled_m = [[0] * cols for c in range(rows)]

for idx, sidx in enumerate(replace_indices):
    shuffled_m[idx//rows][idx%rows] = m[sidx//rows][sidx%rows]

now to unshuffle, you can use the same replace_indices but in reverse:

unshuffled_m = [[0] * cols for c in range(rows)]

for idx, sidx in enumerate(replace_indices):
    unshuffled_m[sidx//rows][sidx%rows] = shuffled_m[idx//rows][idx%rows]

In other words, replace_indices is a symmetric-key of your encryption.

Yevhen Kuzmovych
  • 10,940
  • 7
  • 28
  • 48
1

This seemed like a fun exercise for numpy arrays. My solution is pretty verbose, but I hope it helps illustrate each step.

import numpy as np
import random


in_data = [[10,20,30,40],
            [51,61,71,81],
            [92,102,112,122],
            [133,143,153,163]]

array = np.array(in_data)  # Create array
print('Start array:\n', array)

old_shape = array.shape  # Preserve shape
long_array = np.reshape(array, array.size)  # turn it into a 1xN vector
print('long_array: ', long_array)


index = np.arange(0, array.size) # Create a simple 0-N index
print('index: ', index)
random.shuffle(index)  # Shuffle the index
print('shuffled index: ', index)

long_array = long_array[index]  # Apply shuffled index to the data array
print('shuffled long array: ', long_array[index])

shuffled_array = long_array.reshape(old_shape) # Create the 4x4 shape using the old_shape

print('reshaped shuffled array:\n', shuffled_array)

### UNSHUFFLING
# Generate a new linear index and sort the shuffled index to obtain a reverse sort
new = np.vstack([index, np.arange(0,len(index))])
new = new.T

unshuffled_index = new[new[:,0].argsort()].T[1,:]  # Sort the shuffled_index column
print("unshuffled_index: ", unshuffled_index)

# Condensed reshape, re-index, re-shape of the shuffled data
orig = np.reshape(np.reshape(shuffled_array, array.size)[unshuffled_index], old_shape)


print('original data:\n', orig)

This would be much cleaner if I could avoid the reshaping to 1xN and/or had a more clever way to reverse the shuffle.

Correy Koshnick
  • 211
  • 1
  • 8