Jeśli masz bardzo mało cykli, oto algorytm, który zużyje mniej miejsca, ale jego zakończenie zajmuje znacznie więcej czasu.
[Edytuj.] W mojej poprzedniej analizie w czasie wykonywania pominięto kluczowy koszt ustalenia, czy odwiedzane przez nas węzły należą do tych, z których wcześniej pobrano próbki; ta odpowiedź została nieco poprawiona, aby to poprawić.
Znów iterację wszystkich elementów S . Jak badamy orbity z elementów s ∈ S , to próbki z węzłów, które my odwiedziliśmy, aby być w stanie sprawdzić, czy możemy się z nimi ponownie. Prowadzimy również listę próbek „składników” - związków orbit, które kończą się we wspólnym cyklu (i dlatego są równoznaczne z cyklami) - które były wcześniej odwiedzane.
Zainicjować pustą listę składników complist
. Każdy komponent jest reprezentowany przez zbiór próbek z tego komponentu; utrzymujemy również drzewo wyszukiwania, samples
które przechowuje wszystkie te elementy, które zostały wybrane jako próbki dla jakiegoś komponentu lub innego. Niech G będzie ciągiem liczb całkowitych do n , dla których członkostwo można skutecznie określić przez obliczenie jakiegoś predykatu logicznego; na przykład potęgi 2 lub idealne p th potęgi dla jakiejś liczby całkowitej p . Dla każdego s ∈ S wykonaj następujące czynności:
- Jeśli s jest
samples
, przejdź do kroku 5.
- Zainicjuj pustą listę
cursample
, iterator j ← f ( s ) i licznik t ← 1.
- Gdy j nie jest w
samples
:
- Jeśli t ∈ G , wstaw j do obu cursample
i samples
.
- Przyrost T i zestaw J ← f (j) .
- Sprawdź, czy j jest włączony
cursample
. Jeśli nie, napotkaliśmy wcześniej zbadany komponent: sprawdzamy, do którego komponentu należy j , i wstawiamy wszystkie elementy cursample
do odpowiedniego elementu, complist
aby go powiększyć. W przeciwnym razie ponownie napotkaliśmy element z bieżącej orbity, co oznacza, że przemierzyliśmy cykl co najmniej raz, nie napotykając żadnych przedstawicieli wcześniej odkrytych cykli: wstawiamy cursample
, jako zbiór próbek z nowo znalezionego komponentu, do complist
.
- Przejść do następnego elementu, s ∈ S .
Dla n = | S |, niech X (n) będzie funkcją monotoniczną zwiększającą opisującą oczekiwaną liczbę cykli ( np. X (n) = n 1/3 ), i niech Y (n) = y (n) log ( n ) ∈ Ω ( X (n) log ( n )) jest monotoniczną funkcją zwiększającą, określającą cel wykorzystania pamięci ( np. Y (n) = n 1/2 ). Wymagamy y (n) ∈ Ω ( X (n) ), ponieważ zajmie co najmniej X (n) log ( n ) miejsca do przechowywania jednej próbki z każdego komponentu.
Im więcej elementów orbity próbkujemy, tym bardziej prawdopodobne jest, że szybko wybieramy próbkę w cyklu na końcu orbity, a tym samym szybko wykrywamy ten cykl. Z asymptotycznego punktu widzenia sensowne jest wówczas uzyskanie tylu próbek, na ile pozwalają nasze granice pamięci: równie dobrze możemy ustawić G, aby mieć oczekiwane elementy y (n) , które są mniejsze niż n .
- Jeśli spodziewana jest maksymalna długość orbity w S, to L , możemy pozwolić, aby G była wielokrotnością całkowitą L / y (n) .
- Jeśli nie ma oczekiwanej długości, możemy po prostu próbkować raz co n / r (n)elementy; jest to w każdym razie górna granica odstępów między próbkami.
Jeśli szukając nowego komponentu, zaczniemy przemierzać elementy S, które wcześniej odwiedziliśmy (albo z nowego odkrytego komponentu, albo ze starego, którego cykl końcowy został już znaleziony), zajmie to najwyżej n / r ( n) iteracje napotkane wcześniej próbkowany element; jest to wtedy górna granica liczby razy, przy każdej próbie znalezienia nowego komponentu przemierzamy zbędne węzły. Ponieważ podejmujemy n takich prób, wówczas nadmiarowo odwiedzimy elementy S łącznie łącznie maksymalnie n 2 / r (n) razy.
Praca wymagana do przetestowania członkostwa samples
to O ( y (n) log y (n) ), którą powtarzamy przy każdej wizycie: skumulowany koszt tego sprawdzenia wynosi O ( n 2 log y (n) ). Istnieje również koszt dodania próbek do ich odpowiednich kolekcji, które łącznie wynoszą O ( y (n) log y (n) ). Wreszcie, za każdym razem, gdy ponownie napotykamy wcześniej wykryty komponent, musimy poświęcić do X (n) log * y (n) czas, aby określić, który komponent został ponownie odkryty; ponieważ może się to zdarzyć nawet n razy, skumulowana praca jest ograniczona przez n X (n) log y (n) .
Zatem łączna praca wykonana w celu sprawdzenia, czy odwiedzane przez nas węzły znajdują się wśród próbek, dominuje w czasie wykonywania: kosztuje to O ( n 2 log y (n) ). Następnie powinniśmy uczynić y (n) tak małym, jak to możliwe, to znaczy O ( X (n) ).
Zatem można wyliczyć liczbę cykli (która jest taka sama jak liczba składników kończących się tymi cyklami) w przestrzeni O ( X (n) log ( n )), biorąc O ( n 2 log X (n) ) czas to zrobić, gdzie X (n) jest oczekiwaną liczbą cykli.