• Nie Znaleziono Wyników

Pokryciem zachłannym ciągu nazywane jest pokrycie, w którym dla każdego symbolu ciągu zachodzi, że indeks listy zawierającej ten symbol jest długością podciągu LIS

PODCIĄGI ROSNĄCE

Definicja 2.4. Pokryciem zachłannym ciągu nazywane jest pokrycie, w którym dla każdego symbolu ciągu zachodzi, że indeks listy zawierającej ten symbol jest długością podciągu LIS

kończącego się na tym symbolu.

Pokrycie zachłanne ma kilka interesujących właściwości, których znajomość będzie pomoc-na w kolejnych rozdziałach:

1. Dla każdego elementu pozycja listy w pokryciu zachłannym, do której ten element należy, jest długością podciągu LIS kończącego się na tym elemencie.

2. Pokrycie zachłanne jest unikalne, tzn. nie istnieje inne pokrycie o własności 1.

3. Pokrycie zachłanne ma minimalny rozmiar, tzn. nie da się zbudować pokrycia o mniejszej liczbie list.

4. Rozmiar pokrycia zachłannego jest długością podciągu LIS dla całego ciągu.

Pokrycie zachłanne ciągu A będzie oznaczane przezΓ(A) lub krócej przezΓ, jeśli z kontek-stu będzie oczywiste, o pokryciu którego ciągu mowa. Listy należące do pokrycia będą nume-rowane począwszy od 1, aΓ(A)[k] będzie oznaczało k-tą listę pokryciaΓ(A). W dalszej części pracy wielokrotnie będą stosowane pokrycia zachłanne ciągów, wobec czego dla zwięzłości opisu za każdym razem, gdy pojawi się termin „pokrycie” ciągu, należy rozumieć przez nie

„pokrycie zachłanne”.

Algorytm Cover-Make tworzący pokrycie ciągu przedstawiony jest na rys. 2.3, a ilustrację jego działania można zobaczyć na rys. 2.4. Algorytm ten przetwarza kolejno symbole ciągu

2.4. Algorytmy 39 Cover-Make(A)

Wejście: A – ciąg, dla którego budowane jest pokrycie Wyjście: pokrycie zachłanne dla ciągu A

1 Γ← pusta lista przechowująca listy uporządkowane malejąco 2 for i ← 1 to n do

3 k← najmniejszy numer listy zΓ, której element końcowy jest większy niż ai

4 if k istnieje then

5 Wstaw aina koniec listyΓ[k]

6 else

7 Utwórz nową listę zawierającą ai i dołącz ją na koniecΓ 8 returnΓ

Rys. 2.3. Algorytm tworzenia pokrycia zachłannego ciągu Fig. 2.3. Greedy cover making algorithm of the sequence

4 4 4 4 4 4

4 4 4 4 4 4

6 6 6 6 6

6 6 6 6 6 6

2 2 2 2

2 2 2 2 2 2

8 8 8

8 8 8 8 8 8

1 1

1 1 1 1 1 1

3

3 3 3 3 3 3

12 12 12 12 12 12

9 5 9 5 9 5 9 5 9

7 7 7

11 11

10

Rys. 2.4. Przykład działania algorytmu Cover-Make wyznaczającego pokrycie zachłanne ciągu A = 4 6 2 8 1 3 12 9 5 7 11 10. Ostatnio wstawione elementy są zaznaczone na szaro. Listy ułożone są pionowo

Fig. 2.4. Example of the algorithm Cover-Make finding greedy cover of A = 4 6 2 8 1 3 12 9 5 7 11 10. The just placed elements are gray. The lists are vertical

i dla każdego z nich wyszukuje w dotychczas utworzonym pokryciu pierwszą taką listę, której element końcowy jest większy niż element bieżący. Element bieżący jest następnie dołączany do tej listy, dzięki czemu każda z list pokrycia zawiera elementy w porządku malejącym.

Z tak utworzonego pokrycia można w prosty sposób odczytać podciąg LIS (rys. 2.5). Na-leży w tym celu rozpocząć od ostatniej listy, wybrać dowolny element i przesuwać się w kolej-nych iteracjach po każdej liście od przedostatniej do pierwszej wybierając zawsze największy z elementów mniejszych niż ostatnio wybrany. Można wykazać [106], że w taki sposób zawsze z poprzedniej listy wybierany jest element mniejszy niż bieżący, który już się w niej znajdował (a więc musiał wystąpić w ciągu wejściowym wcześniej niż wybrany element z bieżącej listy).

Dowód, że otrzymane w ten sposób pokrycie jest pokryciem zachłannym, można znaleźć w [106]. Łatwo można zauważyć, że w każdym momencie działania algorytmu Cover-Make elementy kończące kolejne listy uporządkowane są rosnąco, co pozwala wyszukiwać odpowied-nią listę w wierszu3. tego algorytmu w szybki sposób. Jeśli nie zakłada się nic o elementach oprócz tego, że można je porównywać ze sobą, to zastosowanie wyszukiwania binarnego bądź zrównoważonych drzew poszukiwań binarnych przechowujących elementy kończące listy po-zwala na osiągnięcie, dla całego algorytmu, złożoności czasowej O(n log`) w przypadku

pesy-LIS-Read(Γ)

Wejście:Γ– pokrycie zachłanne ciągu A Wyjście: najdłuższy podciąg rosnący w ciągu A

1 ` ← |Γ|

2 s`← dowolny element zΓ[`]

3 for i ← |Γ| − 1 downto 1 do

4 si← największy z elementów zΓ[i] mniejszy niż si+1

5 return s1s2. . . s`

Rys. 2.5. Algorytm odczytywania podciągu LIS z pokrycia utworzonego algorytmem Cover-Make Fig. 2.5. An algorithm reading LIS from the cover produced by Cover-Make algorithm

mistycznym, gdzie ` jest długością podciągu LIS dla rozpatrywanego ciągu. W przypadku gdy elementy są liczbami całkowitymi z zakresu [0,σ− 1], jak zakładane jest w niniejszej pracy (w szczególnym przypadku ciąg może być permutacją liczb z przedziału [1, n]), możliwe jest zastosowanie drzew van Emde Boasa [209, 210] i osiągnięcie tym samym złożoności czaso-wej O(n loglogσ) [119, 33].

Łatwo można zauważyć, że z samego pokrycia ciągu nie można w jednoznaczny sposób odtworzyć samego ciągu. Prostym przykładem są tu ciągi A0= 2 3 1 oraz A00= 2 1 3, których pokrycia są identyczne. Nie zmienia to jednak faktu, że pokrycie ciągu niesie z sobą wiele istotnych informacji, które mogą być wykorzystane w konstrukcji różnych algorytmów.

Warto również zaznaczyć, że Fredman [91] wykazał, że złożoność czasowa O(n log`) jest optymalna w modelu obliczeniowym opartym na porównywaniu elementów (zwanym też mo-delem drzew decyzyjnych). Bespamyatnikh i Segal pokazali [33], jak wyznaczyć wszystkie naj-dłuższe podciągi rosnące.

2.5. Podsumowanie

Z ciekawszych przykładów zastosowań algorytmów rozwiązywania problemu LIS można wymienić projekt MUMmer [57, 58, 137], w którym problem LIS jest rozwiązywany w ce-lu uliniowienia całych genomów organizmów żywych. W [84] problem LIS był wykorzystany do tworzenia map genowych. Zhang [223] omawia zastosowanie tego problemu przy odkry-waniu nowych genów w projektach Celera Genomics. Lin i in. [144] dyskutują zastosowania problemu LIS w projektowaniu próbek identyfikujących wirusy. Golab i in. [98] omawiają jego zastosowania do wyznaczania zależności w bazach danych. Algorytm wyznaczania podciągu LIS może być także użyty jako element składowy algorytmu wyznaczania najdłuższego wspól-nego podciągu (ang. longest common subsequence, LCS), który to problem będzie dyskutowany w dalszych rozdziałach niniejszej pracy (podrozdz. 7.3.6). Kolejnym przykładem zastosowań jest wyznaczanie klik w grafach permutacji [100, str. 159].

2.5. Podsumowanie 41 Istnieje wiele wariantów problemu LIS i niektóre z nich będą szczegółowo rozważane w ko-lejnych rozdziałach. Zaproponowanie części z tych wariantów miało podłoże praktyczne, tj. oka-zało się, że bądź to podstawowy problem LIS, bądź pewne jego odmiany mogą zostać w ciekawy sposób wykorzystane do rozwiązania innych problemów.