Although the time complexity is huge, with a small set of 12 items, I can safely guarantee that the code would run in less than a second or two.
In return, this code can provide you pure and unbiased random, and you can generate as many samples as you wish. The more samples you generate and the fewer items the original set has, the less time it would likely take your code to run.
def generate(n, lst):
if n < len(lst) // 3:
print(f'"It is impossible to generate {n} samples."')
else:
while True:
stack = None
for _ in range(n):
tup = np.random.choice(lst, size=3, replace=False)
if stack is None:
stack = tup
else:
stack = np.vstack((stack, tup))
if len(np.unique(stack.flatten())) == len(lst):
return list(tuple(i) for i in stack)
Basically, it generates n samples until the samples meet the condition.
Results
Generating six samples with the code above gives the result.
>>> lst = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
>>> generate(6, lst)
[(5, 0, 2), (8, 1, 4), (0, 4, 7), (10, 7, 2), (9, 11, 2), (3, 6, 1)]
Generating four samples gives the following result.
>>> lst = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
>>> generate(4, lst)
[(7, 10, 11), (0, 4, 3), (1, 8, 5), (2, 9, 6)]
It is impossible to satisfy the condition with three samples.
>>> lst = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]
>>> generate(3, lst)
"It is impossible to generate 3 samples."