The requirements for this one were (original SO question):
- Generate a random-ish sequence of items.
- Sequence should have each item N times.
- Sequence shouldn't have serial runs longer than a given number (longest below).
from random import random from itertools import groupby # For testing the result try: xrange except: xrange = range def generate_quasirandom(values, n, longest=3, debug=False): # Sanity check if len(values) < 2 or longest < 1: raise ValueError # Create a list with n * [val] source =  sourcelen = len(values) * n for val in values: source += [val] * n # For breaking runs serial = 0 latest = None for i in xrange(sourcelen): # Pick something from source[:i] j = int(random() * (sourcelen - i)) + i if source[j] == latest: serial += 1 if serial >= longest: serial = 0 guard = 0 # We got a serial run, break it while source[j] == latest: j = int(random() * (sourcelen - i)) + i guard += 1 # We just hit an infinit loop: there is no way to avoid a serial run if guard > 10: print("Unable to avoid serial run, disabling asserts.") debug = False break else: serial = 0 latest = source[j] # Move the picked value to source[i:] source[i], source[j] = source[j], source[i] # More sanity checks check_quasirandom(source, values, n, longest, debug) return source def check_quasirandom(shuffled, values, n, longest, debug): counts =  # We skip the last entries because breaking runs in them get too hairy for val, count in groupby(shuffled): counts.append(len(list(count))) highest = max(counts) print('Longest run: %d\nMax run lenght:%d' % (highest, longest)) # Invariants assert len(shuffled) == len(values) * n for val in values: assert shuffled.count(val) == n if debug: # Only checked if we were able to avoid a sequential run >= longest assert highest <= longest for x in xrange(10, 1000): generate_quasirandom((0, 1, 2, 3), 1000, x//10, debug=True)
I'd like to receive any input you have on improving this code, from style to comments to tests and anything else you can think of.