permute 'em all
От: Кодт Россия  
Дата: 14.02.18 12:36
Оценка:
Все мы хорошо знаем (а кто не знает, тот не мы или не все), как можно сделать случайную перестановку массива из N элементов:
arr = list(range(N)) # исходный массив
for i in range(0,N-1):             # 0..N-2 - последняя итерация была бы вырожденной, поэтому исключим её.
    j = random.randint(i,N-1)      # равномерно распределённое случайное число из i..N-1
    arr[i],arr[j] = arr[j],arr[i]  # выполняем обмен очередного элемента со случайным

Несложно показать, что если ГСЧ равномерный, то все N! перестановок тоже распределены равномерно.

Правда, есть одна заморочка: если опорный ГСЧ выдаёт числа из фиксированного диапазона (обычно, в разрядной сетке — т.е., например, сишный rand() — 15-битный, 0..32767), — то результирующие 0..2, 0..4, 0..6 и т.д., не являющиеся делителями 2**14, будут чуточку неравномерно распределены.

Задачка 1. Разогревочная.

Если результирующий ГСЧ мы получаем из опорного по формуле вида
minvalue + rand()%(maxvalue+1-minvalue)
или
minvalue + rand()*(maxvalue+1-minvalue)/(RAND_MAX+1)
,
то во сколько раз чаще будем получать наиболее вероятную перестановку, по сравнению с наименее вероятной?

На какой длине массива этот разброс вероятностей достигнет 2? 10?
Будем считать, что опорный ГСЧ — сишный 15-битный.

Кстати, к сишному генератору есть вопросы по поводу автокорреляции (а это важно, когда мы берём не одно значение, а серию).
Поэтому есть лайфхак: сгенерить случайный текст без особых ограничений на корреляции, и зашифровать (или даже захешировать) его более-менее стойким алгоритмом. На выходе будет достаточно качественный белый шум.
И в качестве опорного ГСЧ взять байты этой шифрованной последовательности.

Будем считать, что опорный ГСЧ — вот такой хороший, но 8-битный.
На какой длине массива достигнем 2- и 10-кратного разброса вероятностей?

Задачка 2. Программистская.

По-прежнему, пусть у нас есть опорный ГСЧ — 15- или 8-битный.
Что нужно сделать, чтобы минимизировать разброс вероятностей перестановок? Желательно, детерминированным способом. (А то знаем мы: кидать кубик до тех пор, пока не выпадет число в искомом диапазоне).

Задачка 3. Внезапная.

Пусть мы разжились опорным равномерным ГСЧ 0..N-1 и решили этим воспользоваться.
for i in range(0,N):          # 0..N-1
    j = random.randint(0,N-1) # 0..N-1
    arr[i],arr[j] = arr[j],arr[i]


Мысль здесь такая: выше мы прикладывали воздействие в пространстве из N! операций, а здесь — в пространстве N**N, что гораздо больше факториала. То есть, перемешиваем с некоторым избытком.

Насколько неравномерное распределение перестановок получилось?
Всё тот же вопрос: вероятность моды / вероятность раритета.

Задачка 4. С кучей звёздочек.

Для заданного N получить моду и раритет, то есть, сами перестановки, а не значения их вероятностей.

Как это сделать в случае с первым алгоритмом — факториальным — с дребезжащим ГСЧ — это очень просто. Мы выкрутим вероятности до крайностей и смоделируем.
Например, при взятии остатка число i наиболее вероятно, а число N-1 — наименее. При делении — обратная картина.
mode = list(range(N))
rare = list(range(N))
for i in range(N-2):
    # обмен i,i для моды - вырожден
    rare[i],rare[N-1] = rare[N-1],rare[i]


А вот со вторым случаем природа неравномерности уже не в дребезге ГСЧ, а в самом алгоритме.
И как сгенерировать моду и раритет, я не знаю. Есть ли у кого идеи?
Перекуём баги на фичи!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.