Методы получения случайных комбинаторных объектов — различия между версиями
Cczy (обсуждение | вклад) |
Cczy (обсуждение | вклад) |
||
Строка 75: | Строка 75: | ||
Будем строить разбиение таким образом, чтобы в результате подмножества <tex> \{B_1, B_2, ..., B_k\} </tex> оказались отсортированы в лексикографическом порядке (т.е. чтобы для любых <tex> 1 <= i < j <= k </tex> наименьший элемент <tex> B_i </tex> был меньше наименьшего элемента <tex> B_i </tex>). Для этого будем по очереди добавлять каждое число от <tex> n </tex> до <tex> 1 </tex> в одно из подмножеств и для каждого из подмножеств начиная с <tex> B_n </tex> и заканчивая <tex> B_1 </tex> будем выбирать какой элемент будет добавлен в него последним(т.е. будет минимальным). | Будем строить разбиение таким образом, чтобы в результате подмножества <tex> \{B_1, B_2, ..., B_k\} </tex> оказались отсортированы в лексикографическом порядке (т.е. чтобы для любых <tex> 1 <= i < j <= k </tex> наименьший элемент <tex> B_i </tex> был меньше наименьшего элемента <tex> B_i </tex>). Для этого будем по очереди добавлять каждое число от <tex> n </tex> до <tex> 1 </tex> в одно из подмножеств и для каждого из подмножеств начиная с <tex> B_n </tex> и заканчивая <tex> B_1 </tex> будем выбирать какой элемент будет добавлен в него последним(т.е. будет минимальным). | ||
− | На каждом шаге префиксом считаем текущее разбиение. Оно характеризуется двумя значениями: <tex> l </tex> — число добавленных элементови и <tex> m </tex> — число подмножеств для которых определен последний элемент. Заметим, что количество разбиений на подмножества с заднным префиксом равно числу способов разбить еще не добавленные элементы на еще не законченные подмножества то есть равно числу разбиений <tex> n-l </tex> элементов на <tex> k-m </tex> непустых подмножеств, что равно <tex dpi = "180">\lbrace{n-l\atop k-m}\rbrace</tex> (т.е [[Числа Стирлинга второго рода|числу Стирлинга второго рода]]). | + | На каждом шаге префиксом считаем текущее разбиение. Оно характеризуется двумя значениями: <tex> l </tex> — число добавленных элементови и <tex> m </tex> — число подмножеств для которых определен последний элемент. Заметим, что количество разбиений на подмножества с заднным префиксом равно числу способов разбить еще не добавленные элементы на еще не законченные подмножества то есть равно числу разбиений <tex> n-l </tex> элементов на <tex> k-m </tex> непустых подмножеств, что равно <tex dpi = "180">\lbrace{n-l\atop k-m}\rbrace</tex> (т.е [[Числа Стирлинга второго рода|числу Стирлинга второго рода]]). Таким образом из префикса <tex> P </tex> мы можем получить следующий префикс <tex> P' </tex> двумя способами: |
+ | *Добавить текущий элемент (<tex> n-l </tex>) в одно из <tex> k-m </tex> незаполненых подмножеств. В таком случае число обьектов с префиксом <tex> P' </tex> будет равно <tex dpi = "180">\lbrace{n-l-1\atop k-m}\rbrace</tex> . | ||
+ | *Сделать текущий элемент последним в подмножестве <tex> B_{k-m} </tex> . В таком случае это подмножество станет заполненым, следовательно число обьектов с префиксом <tex> P' </tex> будет равно <tex dpi = "180">\lbrace{n-l-1\atop k-m-1}\rbrace</tex>. |
Версия 16:35, 8 декабря 2018
Содержание
Описание алгоритма
Задача: |
Необходимо сгенерировать случайный комбинаторный объект размера | с равномерным распределением вероятности, если в наличии есть функция для генерации случайного числа в заданном интервале.
Пусть
- множество различных элементов, которые могут находиться в данном комбинаторном объекте.Будем получать элементы по порядку: сначала определим, какой элемент будет стоять на первом месте, потом на втором и так далее. Считаем, что мы построили префикс длинны
: . Будем выбирать элемент из множества всех возможных так, чтобы вероятность выбора элемнта , была пропорциональна числу комбинторных обьектов размера с префиксом . Для этого разобъем отрезок натуральных чисел . где - число различных комбинаторных объектов с текущим префиксом, на диапазонов так, чтобы размер диапазаоны был равен числу объектов с префиксом . С помощью функция для генерации случайного числа получим число в интервале и добавим к префиксу элемент соответствующий диапазону отрезка в которм находится полученное число.object randomObject(n: int, k: int): //— размер комбинторного объекта, — число различных элемнтов. for i = 1 to n s = number(prefix) // число комбинаторных объектов с текущим префиксом. r = random(1, s) for j = 1 to k if number(prefix + B[j]) < r // — множество всех возможных элементов. r = r - number(prefix + B[j]) // если не попало в текщий диапазон — перейдем к следующему. else prefix[i] = b[j] break return prefix
Сложность алгоритма —
. Количества комбинаторных объектов с заданными префиксами считаются известными, и их подсчет в сложности не учитывается. Стоит отметить, что подсчет количества комбинаторных объектов с заданным префиксом зачастую является задачей с достаточно большой вычислительной сложностью.Доказательство корректности
Докажем по индукции, что вероятность получить любой перфикс
равна , где - число различных комбинаторных данного типа длины , а - число различных комбинаторных обьектов с таким префиксом.- Любой комбинаторный объеут имеет пустой перфикс, следовательно . Вероятность получить любой префикс длины равна , что равно .
- Пусть вероятность получить префикс длины равна
- Тогда вероятность получить из любой префикс длины равна , что равно
Битовые вектора
Рассмотрим алгоритм получения случайного битового вектора. В битовом векторе может находиться только два типа элементов:
и , следовательно . Заметим что для любого префикса длины число возможных комбинаторных объектов одинаково и равно, следовательно на каждом шаге алгоритма небходмо выбирать с равной вероятностью илиvector<int> randomBitVector(n: int): //
— размер битового вектора.
for i = 1 to n
r = random(0, 1)
v[i] = r
return prefix
Сложность алгоритма —
, так как в случае двоичных векторов постоянно и равно .Правильные скобочные последовательности
Рассмотрим алгоритм получения случайной правильной скобочной последовательности. Правильная скобочная пследовательность состоит из двух типов элементов: открывающей и закрывающей скобок, следовательно
.Рассмотрим "полуправильные" скобочные последовательности т.е. такие что всякой закрывающей скобке соответствует парная открывающая, но не все открытые скобки закрыты. Такую последовтеьность можно охарактеризовать двумя числами:
— длина скобочной последовательности и — баланс (т.е. разность между количеством открывающих и закрывающих скобок). Заметим что любой префикс правильной скобочной последовательности является "полупраильной" скобочной последовательностью, и что для любого префикса длины число различных ПСП длины равно числу "полуправильных" скобочных последовательностей длины с таким же балансом как у .Научимся считать
— число последовательностей длины и баланса ). Если , то ответ понятен сразу: , все остальные . Пусть теперь , тогда переберём, чему мог быть равен последний символ этой последовательности. Если он был равен , то до этого символа мы находились в состоянии . Если он был равен , то предыдущим было состояние . Таким образом, получаем формулу:
(считается, что все значения
при отрицательном равны нулю). Этот преподсчет можно выполнить за .Будем строить префикс следующим образом: на каждом шаге интервал случайных чисел
(где ) , будет разбиваться на два диапазона размерами и , и к префиксу будет прибавляться или если случайное число попало в первый или второй диапазон соответственно.string randomObject(n: int): //
— длина скобочной последовательности.
b = 0
l = 0
for i = 1 to n
s = number[n - l][b]
r = random(1, s)
if number[n - l - 1][b + 1] >= r
l = l + 1
b = b + 1
result = result + '('
else
l = l + 1
b = b - 1
result = result + ')'
return result
Итоговая сложность алгоритма —
на преподсчет.Разбиения на множества
Разбиение на подмножеств
Рассмотрим множество первых
натуральных чисел: . Необходимо разбить его на непустых подмножеств с равным распределением вероятности.Будем строить разбиение таким образом, чтобы в результате подмножества
оказались отсортированы в лексикографическом порядке (т.е. чтобы для любых наименьший элемент был меньше наименьшего элемента ). Для этого будем по очереди добавлять каждое число от до в одно из подмножеств и для каждого из подмножеств начиная с и заканчивая будем выбирать какой элемент будет добавлен в него последним(т.е. будет минимальным).На каждом шаге префиксом считаем текущее разбиение. Оно характеризуется двумя значениями: числу Стирлинга второго рода). Таким образом из префикса мы можем получить следующий префикс двумя способами:
— число добавленных элементови и — число подмножеств для которых определен последний элемент. Заметим, что количество разбиений на подмножества с заднным префиксом равно числу способов разбить еще не добавленные элементы на еще не законченные подмножества то есть равно числу разбиений элементов на непустых подмножеств, что равно (т.е- Добавить текущий элемент ( ) в одно из незаполненых подмножеств. В таком случае число обьектов с префиксом будет равно .
- Сделать текущий элемент последним в подмножестве . В таком случае это подмножество станет заполненым, следовательно число обьектов с префиксом будет равно .