Пошук усіх циклів


9

У мене є кінцеве безліч , функція , і в загальній складності близько на . Я хочу , щоб знайти число різних циклів в .Sf:SS<SS

Для заданого елемента я можу використовувати алгоритм Флойда (або Brent's тощо), щоб знайти тривалість циклу, на який повторюються програми відправляють ; з трохи більше зусиль я можу визначити цей цикл (наприклад, за його -мінімальним елементом). Поганим методом вирішення проблеми буде повторення цього кожного елемента, сортування отриманих мінімальних елементів, відкидання дублікатів, і повернення підрахунку. Але це потенційно передбачає багато проходів над одними і тими ж елементами та великими вимогами до простору.sSfs<

Які методи мають кращі показники часу та простору? Я навіть не впевнений, який найкращий спосіб виміряти необхідний простір - якщо функція ідентичності, то будь-який метод, що зберігає всі цикли, буде використовувати простір.fΩ(n)


4
Один із природних способів вимірювання простору - розглянути S як сукупність n-бітових рядків, а f - як оракул. Тоді наївний алгоритм, який ви описали, вимагає експоненціального простору. Можна шукати алгоритм, який використовує лише поліноміальний простір, але це не здається мені можливим.
Цуйосі Іто

Ось що я мав на увазі під «не знаю, який найкращий спосіб виміряти простір». Можливо, я повинен орієнтуватися на O (poly (n) + y), де y є вихід, так що використаний простір є многочленом до тих пір, поки y достатньо малий.
Чарльз

Чи має ваша функція f корисні властивості? Є чи алгоритм поліноміальний або експоненціальне віддається перевага вами спосіб вираження розміру вхідного буде дещо суперечливе , якщо практичну відповідь в тому , що алгоритм займе час і простір порядку потужності S .
Niel de Beaudrap

@Niel de Beaudrap: Я не впевнений, які властивості були б корисні. Я очікую, що кількість чітких циклів невелика, хоча, ймовірно, ; тому я запропонував функцію і замість просто . Я готовий використовувати експоненціальний простір у кількості вихідних бітів, якщо потрібно. O(n3)ynn
Чарльз

Відповіді:


7

Якщо все, що ви хочете зробити, це підрахувати кількість циклів, ви можете зробити це за допомогою 2 | S | біт (плюс зміна) варто місця. Навряд чи вам вдасться зробити набагато краще, якщо S або f не матиме деяких особливо зручних властивостей.

Почніть з масиву Зберігання цілих чисел {0,1,2} - одне на елемент S - ініціалізовано до нуля; ми вкажемо їх як (unexplored), (partially explored)і (fully explored). Ініціалізуйте лічильник циклів до нуля. Для кожного елемента s  ∈  S по порядку виконайте наступне:

  1. Якщо A [ s ] =  (fully explored), перейдіть до кроку 6.
  2. Встановіть A [ s ] ←  (partially explored)і встановіть ітератор j  ←  f (s) .
  3. Тоді як A [ j ] =  (unexplored), встановіть A [ j ] ←  (partially explored), а задайте j  ←  f (j) .
  4. Якщо A [ j ] =  (partially explored), ми закрили новий цикл; приріст c на 1. (Якщо ви хочете вести облік деякого представника цього циклу, поточне значення j буде виконуватись як довільний вибір; звичайно, це не обов'язково буде мінімальним елементом циклу відповідно до бажаного порядок <.) В іншому випадку ми маємо A [ j ] =  (fully explored), це означає, що ми виявили попередньо досліджувану орбіту, яка закінчується вже переліченим циклом; не збільшують c .
  5. Щоб вказати, що орбіта, що починається з s , тепер повністю вивчена, встановіть j  ←  s .
    Тоді як A [ j ] =  (partially explored), встановіть A [ j ] ←  (fully explored)і встановіть j  ←  f (j) .
  6. Перейдіть до іншого сек  ∈  S .

Таким чином, кожен цикл серед орбіт, індукованих 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 .)


Відмінно, це покращується на дурному алгоритмі простору , який я мав на увазі. Мені насправді не потрібні представники; Я ввів всяк випадок, якщо це було б корисно якомусь алгоритму. O(|S|log|S|)<
Чарльз

Цікаво, чи є спосіб використовувати набагато менше місця в тому випадку, коли загальних циклів мало, не використовуючи більше поліноміального простору. Ах, неважливо; це зробить для моїх потреб.
Чарльз

1
Мені здається, що це має бути в #L (з використанням матричного живлення). Це може бути # L-важко?
Каве

@Charles: дивіться мою останню відповідь, яка дасть вам покращення, якщо ви знаєте, що #cycles ∈ o ( | S | ). Він використовує більше, ніж полілог | S | місця, але якщо ви готові торгувати простором та часом, можливо, вам це буде краще.
Ніль де Бодорап

@Niel de Beaudrap: Дякую! +1 для обох. Цей алгоритм здається найкращим, доки дані вміщуються в пам'яті; як тільки він розпливеться, я погляну на використання іншого. (Можливо, що іншим було б краще, якби я міг вмістити все в кеш, але це може бути занадто багато клопоту.)
Чарльз

5

Якщо у вас дуже мало циклів, ось алгоритм, який використовуватиме менше місця, але для його завершення потрібно значно більше часу.

[Редагувати] Мій попередній аналіз часу пропустив вирішальну вартість визначення того, чи є вузли, які ми відвідуємо, серед тих, що раніше були відібрані; ця відповідь була дещо переглянута, щоб виправити це.

Ми знову ітерація через всі елементи S . Коли ми досліджуємо орбіти елементів s  ∈  S , ми робимо вибірку з побудованих нами вузлів, щоб можна було перевірити, чи стикаємося ми знову. Ми також ведемо список зразків із "компонентів" - об'єднань орбіт, які закінчуються в загальному циклі (і, отже, рівнозначні циклам), які раніше були відвідані.

Ініціалізувати порожній список компонентів, complist. Кожен компонент представлений колекцією зразків із цього компонента; ми також підтримуємо дерево пошуку, samplesяке зберігає всі ті елементи, які були обрані як зразки для того чи іншого компонента. Нехай G - послідовність цілих чисел до n , для яких належним чином визначити належність шляхом обчислення деякого булевого предиката; наприклад, потужності 2 або досконалі p- й потужності для деякого цілого p . Для кожного s  ∈  S зробіть наступне:

  1. Якщо s знаходиться samples, перейдіть до кроку №5.
  2. Ініціалізуйте порожній список cursample, ітератор j  ← f ( s ) та лічильник t  ← 1.
  3. Якщо j не в samples:
    - Якщо t  ∈  G , вставте j в обидва cursampleі samples.
    - приріст t і задаємо j  ←  f (j) .
  4. Перевірте, чи є jcursample . Якщо ні, то ми зіткнулися з раніше вивченим компонентом: ми перевіряємо, до якого компонента j належить, і вставляємо всі елементи cursampleу відповідний елемент, complistщоб його доповнити. В іншому випадку ми знову зіткнулися з елементом з поточної орбіти, це означає, що ми пройшли цикл хоча б один раз, не зустрічаючи жодних представників раніше виявлених циклів: ми вставляємо cursample, як колекцію зразків із щойно знайденого компонента, в complist.
  5. Перейдіть до іншого сек  ∈  S .

Для n  = | 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) - очікувана кількість циклів.


1

Ви можете уникнути декількох проходів по одних і тих же елементах, використовуючи структуру даних набору об'єднань . По одному проходу над кожним елементомs з'єднання набору, що містить s з набором, що містить f(s). Це все ще використовує багато пам'яті.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.