Вот идея алгоритма, который может работать на вас. Создайте расстройство в циклической записи. Таким образом, (1 2) (3 4 5)
представляет расстройство 2 1 4 5 3
. (То есть (1 2)
- это цикл, как и (3 4 5)
.)
Поместите первый элемент на первое место (в обозначении цикла вы всегда можете это сделать) и выберите случайную перестановку остальных. Теперь нам просто нужно выяснить, где круглые скобки go для длины цикла.
Как отмечает https://mathoverflow.net/questions/130457/the-distribution-of-cycle-length-in-random-derangement, в перестановке случайный цикл равномерно распределен по длине. Они не случайным образом распределены в расстройствах. Но число отклонений длины m
равно m!/e
, округляется в большую сторону для четных m и уменьшается до нечетных m
. Итак, что мы можем сделать, это выбрать длину, равномерно распределенную в диапазоне 2..n
, и принять ее с вероятностью того, что оставшиеся элементы, действуя случайным образом, будут являться нарушением. Эта длина цикла будет правильно распределена. И затем, как только у нас будет первая длина цикла, мы повторяем следующую до тех пор, пока не закончим.
Процедура, выполненная описанным мною способом, проще в реализации, но математически эквивалентна принятию случайного отклонения (отклонением), и записывать только первый цикл. Потом повторюсь. Поэтому можно доказать, что это вызывает все нарушения с равной вероятностью.
При таком наивном подходе мы будем брать в среднем 3 броска, прежде чем принять длину. Однако затем мы в среднем разрешили проблему пополам. Таким образом, число случайных чисел, которые нам нужно сгенерировать для размещения скобок, равно O(log(n))
. По сравнению со O(n)
случайными числами для построения перестановки это ошибка округления. Однако его можно оптимизировать, отметив, что наибольшая вероятность принятия составляет 0.5
. Поэтому, если мы с двойной вероятностью примем вероятность случайного получения расстройства, если мы продолжим, наша крыса ios будет по-прежнему правильной, и мы избавимся от большинства наших отклонений длины цикла.
Если большую часть времени тратится в генераторе случайных чисел, для больших n
он должен работать примерно в 3 раза быстрее, чем метод отклонения. На практике это не так хорошо, потому что переключение с одного представления на другое на самом деле не бесплатно. Но вы должны получить ускорения на порядок, который вы хотели.