У меня есть конечное множество , функция , и в общей сложности порядка на . Я хочу , чтобы найти число различных циклов в .
Для данного элемента я могу использовать алгоритм Флойда (или Брента и т. Д.), Чтобы найти длину цикла, в который повторные применения посылают ; с немного большим усилием я могу идентифицировать этот цикл (например, по его элементу -minimal). Плохой метод решения проблемы - повторять каждый элемент, сортировать полученные минимальные элементы, исключая дубликаты, и возвращать счетчик. Но это потенциально включает в себя много проходов по одним и тем же элементам и большие требования к пространству.
Какие методы имеют лучшие временные и пространственные характеристики? Я даже не уверен, каков лучший способ измерить необходимое пространство - если - это функция тождества, то любой метод, который хранит все циклы, будет использовать пространство .
источник
Ответы:
Если все, что вы хотите сделать, это подсчитать количество циклов, вы можете сделать это с помощью 2 | S | бит (плюс изменение) стоит места. Маловероятно, что вы сможете добиться большего успеха, если S или f не обладают какими-то особенно удобными свойствами.
Начните с массива A, хранящего целые числа {0,1,2} - по одному на элемент S - инициализируется нулем; мы укажем их как
(unexplored)
,(partially explored)
и(fully explored)
. Инициализировать счетчик циклов на ноль. Для каждого элемента s ∈ S по порядку сделайте следующее:(fully explored)
, перейдите к шагу 6.(partially explored)
и установите итератор j ← f (s) .(unexplored)
, установите A [ j ] ←(partially explored)
и установите j ← f (j) .(partially explored)
, мы замкнули новый цикл; увеличение c на 1. (Если вы хотите сохранить запись какого-либо представителя этого цикла, текущее значение j будет произвольным выбором; конечно, это не обязательно будет минимальным элементом в цикле под вашим предпочтением order <.) В противном случае мы имеем A [ j ] =(fully explored)
, что означает, что мы обнаружили предварительно исследованную орбиту, которая заканчивается в уже подсчитанном цикле; не увеличивать c .Пока A [ j ] =
(partially explored)
, установите A [ j ] ←(fully explored)
и установите j ← f (j) .Таким образом, каждый цикл среди орбит, индуцированных f, будет учитываться один раз; и любые элементы, которые вы записали в качестве представителей, будут элементами разных циклов. Требования к памяти 2 | S | для массива A, O (log | S |) для счетчика циклов и других шансов и результатов.
Каждый элемент s ∈ S будет посещаться как минимум дважды: один раз, когда значение A [ s ] изменяется с
(unexplored)
на(partially explored)
, и один раз для изменения на(fully explored)
. Общее число повторных посещений любых узлов после того, как они были(fully explored)
выше, ограничено числом попыток найти новые циклы, которые не могут это сделать, что составляет самое большее | S | - вытекающие из основного контура итерации по всем элементам S . Таким образом, мы можем ожидать, что этот процесс будет включать не более 3 | S | обход узлов, считая все посещения или повторные посещения узлов.Если вы хотите отслеживать репрезентативные элементы циклов, и вы действительно хотите, чтобы они были минимальными элементами, вы можете ограничить количество посещений узлов 4 | S |, если вы добавите дополнительный «круг вокруг цикла» на шаге 4, чтобы найти представителя, который меньше, чем тот, где вы закрываете цикл. (Если бы орбиты под f состояли только из циклов, этой дополнительной работы можно было бы избежать, но это не относится к произвольным f .)
источник
Если у вас очень мало циклов, вот алгоритм, который будет использовать меньше места, но для его завершения потребуется значительно больше времени.
[Правка.] Мой предыдущий анализ во время выполнения упустил решающую стоимость определения того, входят ли посещаемые нами узлы в число ранее выбранных; этот ответ был несколько пересмотрен, чтобы исправить это.
Мы снова итерация через все элементы S . Когда мы исследуем орбиты элементов s ∈ S , мы выбираем из узлов, которые мы посетили, чтобы иметь возможность проверить, сталкиваемся ли мы с ними снова. Мы также ведем список выборок из «компонентов» - объединений орбит, которые заканчиваются в общем цикле (и, следовательно, равных количеству циклов) - которые были посещены ранее.
Инициализировать пустой список компонентов
complist
. Каждый компонент представлен коллекцией образцов из этого компонента; мы также поддерживаем дерево поиска, вsamples
котором хранятся все элементы, которые были выбраны в качестве образцов для какого-либо компонента. Пусть G - последовательность целых чисел вплоть до n , для которой членство эффективно определяется путем вычисления некоторого логического предиката; например, степени 2 или совершенные p- й степени для некоторого целого числа p . Для каждого s ∈ S сделайте следующее:samples
, перейдите к шагу 5.cursample
, итератор j ← f ( s ) и счетчик t ← 1.samples
:- Если t ∈ G , вставьте j в оба
cursample
иsamples
.- Увеличьте t и установите j ← f (j) .
cursample
. Если нет, мы столкнулись с ранее исследованным компонентом: мы проверяем, к какому компоненту j принадлежит, и вставляем все элементыcursample
в соответствующий элемент,complist
чтобы дополнить его. В противном случае мы повторно встретили элемент с текущей орбиты, что означает, что мы прошли цикл хотя бы один раз, не встретив каких-либо представителей ранее обнаруженных циклов: мы вставляемcursample
, как набор выборок из недавно найденного компонента, вcomplist
.Для п = | S |, пусть X (n) - монотонно возрастающая функция, описывающая ожидаемое количество циклов ( например, X (n) = n 1/3 ), и пусть Y (n) = y (n) log ( n ) ∈ Ω ( X (n) log ( n )) - монотонно возрастающая функция, определяющая цель использования памяти ( например, y (n) = n 1/2 ). Нам требуется y (n) ∈ Ω ( X (n) ), потому что для хранения одного образца из каждого компонента потребуется как минимум X (n) log ( n ) место.
Чем больше элементов орбиты мы выбираем, тем больше у нас шансов быстро выбрать образец в цикле в конце орбиты и тем самым быстро обнаружить этот цикл. С точки зрения асимптотики, тогда имеет смысл получить столько выборок, сколько позволяют наши границы памяти: мы также можем установить в G ожидаемый y (n) элементов, которые меньше n .
- Если максимальная длина орбиты в S ожидается равной L , мы можем позволить G быть целыми числами, кратными L / y (n) .
- Если ожидаемой длины нет, мы можем просто сделать выборку один раз каждые n / y (n)элементы; в любом случае это верхняя граница для интервалов между выборками.
Если при поиске нового компонента мы начнем проходить элементы S, которые мы посетили ранее (либо из обнаруженного нового компонента, либо из старого, у которого конечный цикл уже был найден), это займет не более n / y ( n) итерации, чтобы встретить ранее выбранный элемент; тогда это верхняя граница числа раз, при каждой попытке найти новый компонент мы пересекаем избыточные узлы. Поскольку мы предпринимаем n таких попыток, мы затем будем избыточно посещать элементы S не более n 2 / y (n) раз в общей сложности.
Работа, необходимая для проверки членства,
samples
- это O ( y (n) log y (n) ), которую мы повторяем при каждом посещении: совокупная стоимость этой проверки составляет O ( n 2 log y (n) ). Существует также стоимость добавления образцов в их соответствующие коллекции, которые в совокупности составляют O ( y (n) log y (n) ). Наконец, каждый раз, когда мы снова сталкиваемся с ранее обнаруженным компонентом, мы должны тратить до X (n) log * y (n) времени, чтобы определить, какой компонент мы повторно открыли; так как это может происходить до n раз, кумулятивная работа ограничена n X (n) log y (n) .Таким образом, совокупная работа, выполненная при проверке того, находятся ли узлы, которые мы посещаем, среди выборок, доминирует во время выполнения: это стоит O ( n 2 log y (n) ). Тогда мы должны сделать y (n) как можно меньшим, то есть O ( X (n) ).
Таким образом, можно перечислить количество циклов (которое совпадает с числом компонентов, заканчивающихся в этих циклах) в пространстве O ( X (n) log ( n )), принимая O ( n 2 log X (n) ) время для этого, где X (n) - ожидаемое количество циклов.
источник
Вы можете избежать нескольких проходов через одни и те же элементы, используя структуру данных набора объединений . Один проход по каждому элементу объединяет множество, содержащее с множеством, содержащим . Это все еще использует много памяти.s s f(s)
источник