You just need to create a "baseline" grid, where no two rows or columns are the same. For example, for a grid of size 4x4, the baseline would be:
[[1, 2, 3, 4],
[4, 1, 2, 3],
[3, 4, 1, 2],
[2, 3, 4, 1]]
Then shuffle the rows, then shuffle the columns.
Pure python:
random.shuffle only shuffles the elements of the list you passed. In order to shuffle columns, you will need to transpose the list-of-lists such that the first axis groups columns instead of rows.
import random
def random_grid(size=4):
r = list(range(1, size+1))
baseline = [r[-n:] + r[:-n] for n in range(size)]
random.shuffle(baseline)
transpose = [list(col) for col in zip(*baseline)]
random.shuffle(transpose)
return transpose
Using numpy:
np.random.shuffle only shuffles the first axis, so we transpose the array before shuffling it another time.
import numpy as np
def random_grid(size=4):
r = list(range(1, size+1))
baseline = np.array([r[-n:] + r[:-n] for n in range(size)])
np.random.shuffle(baseline)
np.random.shuffle(baseline.T)
return baseline
Now, since you want random numbers between 0 and size, you can just get a grid that is one row and column larger than what you want, subtract one from every element, and discard the extra row and column.
Pure python:
def random_grid_2(size=4):
rgrid = random_grid(size+1)
return [[i - 1 for i in row[:-1]] for row in grid[:-1]]
Numpy:
def random_grid_2(size=4):
rgrid = random_grid(size+1) - 1
return rgrid[:-1, :-1]
Of course, this approach is less efficient than doing it in random_grid in the first place, when you are creating the grid, but I have kept the two steps separate to help you understand what is happening.