Вы можете попробовать подход оптимизации: начните с списка, содержащего элементы в правильной пропорции, затем продолжайте менять случайные элементы, пока не получите желаемые результаты. В каждом ходу проверяйте количество слишком длинных полос 0 или 1 и всегда сохраняйте лучший из исходного или мутированного списка.
import itertools, random
def penalty(lst):
return sum(1 for k, g in itertools.groupby(lst)
if k == 0 and len(list(g)) > 4 or k == 1 and len(list(g)) > 2)
def constrained_shuffle(lst):
# penalty of original list
p = penalty(lst)
while p > 0:
# randomly swap two elements, get new penalty
a, b = random.randrange(len(lst)), random.randrange(len(lst))
lst[a], lst[b] = lst[b], lst[a]
p2 = penalty(lst)
if p2 > p:
# worse than before, swap back
lst[a], lst[b] = lst[b], lst[a]
else:
p = p2
lst = [0] * 20 + [1] * 10
random.shuffle(lst)
constrained_shuffle(lst)
print(lst)
Для 200 0 и 100 1 это займет несколько от сотен до нескольких тысяч итераций, пока он не найдет правильный список, что нормально. Для списков с тысячами элементов это слишком медленно, но, вероятно, его можно улучшить, запомнив позиции слишком длинных полос и предпочтительно поменяв их местами.
О «случайности» подход: Конечно, это менее случайно, чем просто многократное создание нового перетасованного списка до тех пор, пока один из них не будет соответствовать ограничениям, но я не вижу, как это создаст смещение для или против определенных списков, если они удовлетворяют ограничениям. Я провел короткий тест, многократно генерируя перетасованные списки и подсчитывая частоту появления каждого варианта:
counts = collections.Counter()
for _ in range(10000):
lst = [0] * 10 + [1] * 5
random.shuffle(lst)
constrained_shuffle(lst)
counts[tuple(lst)] += 1
print(collections.Counter(counts.values()).most_common())
[(7, 197), (6, 168), (8, 158), (9, 157), (5, 150), (10, 98), (4, 92),
(11, 81), (12, 49), (3, 49), (13, 43), (14, 23), (2, 20), (15, 10),
(1, 8), (16, 4), (17, 3), (18, 1)]
Итак, да, может быть, есть несколько списков, которые более вероятны, чем другие (один появился 18 раз, три 17 раз, а большинство других 5-9 раз). Для 100 000 итераций «более вероятные» списки появляются на ~ 50% чаще, чем остальные, но все еще только в 120 раз из этих 100 000 итераций, поэтому я думаю, что это не слишком большая проблема.
Без начального random.shuffle(lst)
существует больше списков, которые появляются гораздо чаще, чем в среднем, поэтому не следует пропускать.