I doubt this is the best way, but this is the way to do it. If you think of your input as a type matrix
a1, b1, c1, d1 a2, b2, c2, d2 a3, b3, c3, d3 a4, b4, c4, d4
Then you will begin to choose a random index at each iteration so that the new index is not in the same row or in the same column of the matrix as the previous index, and such that the new element was not previously selected. By entering this into the code naively, it becomes
import random shapes_and_colors=["a1","a2","a3","a4","b1","b2","b3","b4","c1","c2","c3","c4","d1","d2","d3","d4"] nRows = 4 nCols = 4 inds = [(x,y) for x in range(nRows) for y in range(nCols)] def make_random(someArr): toRet = [] n = len(someArr) for i in range(n): possible = [thing for thing in someArr if thing not in toRet] prev = poss = None while poss is None: next_val = random.choice(possible) if next_val == prev: #failed so try again return make_random(someArr) if not toRet or (next_val[0] != toRet[-1][0] and next_val[1] != toRet[-1][1]): poss = next_val prev = next_val toRet += poss, return toRet ans= [thing for thing in make_random(shapes_and_colors)] print ans
Outputs after pair start
['c3', 'd4', 'c1', 'd3', 'b1', 'a4', 'b3', 'c4', 'a3', 'b2', 'a1', 'c2', 'd1', 'a2', 'b4', 'd2'] ['d4', 'b3', 'c1', 'a4', 'b2', 'c4', 'd3', 'a1', 'c3', 'a2', 'b4', 'd2', 'a3', 'b1', 'c2', 'd1']
Renouncement
Since this is an absolutely naive approach, it sometimes gets stuck! Suppose the last two remaining indices are [(2, 2), (3, 2)]. Then there is no way to continue the algorithm without violating the restrictions. Right now I am processing it with a recursive call, which is not ideal.