v wyszukiwania

Transkrypt

v wyszukiwania
Problem (okienkowanie)
Okienkowanie
(windowing)
Wykład 11
2007/01/03
Problem (proste okienkowanie)
Dane:
• zbiór odcinków S na płaszczyźnie, każdy
odcinek poziomy lub pionowy
Cel:
• utworzenie struktury danych pozwalającej
szybko odpowiadać na zapytania z zakresu
Zapytanie z zakresu:
• dane: okno W= [x1,y1]×[x2,y2]
• wynik: wszystkie odcinki s∈S takie, że
s ∩W≠∅
Dane:
• zbiór odcinków S na płaszczyźnie
Cel:
• utworzenie struktury danych pozwalającej
szybko odpowiadać na zapytania z zakresu
Zapytanie z zakresu:
• dane: okno W= [x1,y1]×[x2,y2]
• wynik: wszystkie odcinki s∈S takie, że
s ∩W≠∅
Proste okienkowanie a zapyt. przedz.
Jak znajdować odcinki s∈S, takie że co
najmniej jeden koniec s należy do W?
Odp.: drzewa przedziałowe dla zbioru końców
odcinków
Wydajność:
• pamięć O(n log n)
• preprocessing: O(n log n)
• czas zapytań O(log n + k) – fractional casc.
[O(log2n + k)]
Proste okienkowanie c.d.
Co z odcinkami, których końce poza S?
• interesują nas tylko takie, które przecinają
S, więc przecinają 2 jego krawędzie (obie
pionowe lub obie poziome)
• ograniczmy się do wyszukiwania odcinków
przecinających jedną krawędź (np.
poziomą)... użyjemy interval trees
Odcinki przecinające prostą
Dane: zbiór odcinków poziomych S
Wynik: struktura danych pozwalająca szybko
odpowiadać na pytania:
• dane: prosta pionowa q
• wynik: wszystkie s∈S takie, że s ∩q≠∅
Odcinki przecinające krawędź
Dane: zbiór odcinków poziomych S
Wynik: struktura danych pozwalająca szybko
odpowiadać na pytania:
• dane: odcinek pionowy q
• wynik: wszystkie s∈S takie, że s ∩q≠∅
Interval trees
Dane: zbiór odcinków poziomych I
Interval tree dla I:
• xmid: mediana zbioru końców odcinków
• Ileft={[xj, x’j] ∈I | x’j<xmid}
• Iright={[xj, x’j] ∈I | x’j>xmid}
• Imid={[xj, x’j] ∈I | xj<xmid< x’j}
Interval trees c.d.
• I=∅: IT(I) jest liściem
• I≠∅: v=root(IT(I)) zawiera
– xmid
– Imid posortowany wg końców
– Imid posortowany wg początków
Interval trees: własności
•
•
•
•
Pamięć: O(n)
Głębokość: O(log n)
Czas konstrukcji: O(n log n)
czas zapytania: O(log n + k), gdzie k to
rozmiar odpowiedzi [liczba odcinków]
• left(v)=IT(Ileft)
• right(v)=IT(Iright)
ConstructIntervalTree(I)
•
•
•
•
If I=∅: zwróć pusty liść, wpp
Utwórz wierzchołek v
Wyznacz xmid, Ileft, Iright, Imid
Dołącz do v:
– Lleft(v): Imid posortowany wg początków
– Lright(v): Imid posortowany wg końców
• left(v) ← ConstructIntervalTree(Ileft)
• right(v) ← ConstructIntervalTree(Iright)
Uwaga: przed konstrukcją sort. odc. wg pocz. i końców
QueryIntervalTree(v, qx)
Dane: v – korzeń IT, qx – wsp. pozioma pionowej
prostej
Jeśli v jest liściem: koniec, wpp
• Jeśli qx<xmid(v)
– wypisz wszystkie odcinki Lleft(v), których lewy
koniec mniejszy od xmid(v)
– QueryIntervalTree(left(v), qx)
• Jeśli qx>xmid(v)
– wypisz wszystkie odcinki Lright(v), których prawy
koniec większy od xmid(v)
– QueryIntervalTree(right(v), qx)
Odcinki przecinające krawędź
Dane: zbiór odcinków poziomych S
Wynik: struktura danych pozwalająca szybko
odpowiadać na pytania:
• dane: odcinek pionowy q
• wynik: wszystkie s∈S takie, że s ∩q≠∅
Jak uogólnić interval trees aby rozwiązać
ten problem?
QueryIntervalTree(v, qx)
Dane: v – korzeń IT, qx – wsp. pionowej prostej
Jeśli v jest liściem: koniec, wpp
• Jeśli qx<xmid(v)
– wypisz wszystkie odcinki Lleft(v), których lewy
koniec mniejszy od xmid(v)
– QueryIntervalTree(left(v), qx)
• Jeśli qx>xmid(v)
– wypisz wszystkie odcinki Lright(v), których prawy
koniec większy od xmid(v)
– QueryIntervalTree(right(v), qx)
Odcinki przecinające krawędź
Rozwiązanie:
• zamiast uporządkowanych list Lleft(v) i
Lright(v) [porządek po początkach/końcach],
w węźle v przechowujemy....
• drzewo przedziałowe (range tree) RT(Imid)
dla zbioru Imid
• a algorytm wyszukiwania zmienia się
następująco:
QueryMIntervalTree(v, qx)
Dane: v – korzeń IT, qx, [qy,q’y]: odcinek pionowy
• Jeśli qx< xmid(v)
– wypisz wynik zapytania , (-∝,qx]×[qy,q’y] na
RT(Imid)
– QueryMIntervalTree(left(v), qx)
• Jeśli qx> xmid(v)
– wypisz wynik zapytania , [qx, +∝), ×[qy,q’y] na
RT(Imid)
– QueryMIntervalTree(right(v), qx)
Interval trees: własności po modyf.
•
•
•
•
Pamięć: O(n log n)
Głębokość: O(log n)
Czas konstrukcji: O(n log n)
czas zapytania: O(log2 n + k), gdzie k to
rozmiar odpowiedzi [liczba odcinków]
bez fractional cascading: O(log3 n + k)
Drzewa priorytetowe
Dane: zbiór punktów na płaszczyźnie P.
Drzewo priorytetowe T dla P:
• Jeśli P= ∅: T jest pustym liściem, wpp
•
•
•
•
•
pmin ← punkt w P o min. wart. wsp. x
ymid ← mediana współrzędnych y w P
Pbelow ← {p∈P\{pmin} | p.y < ymid }
Pabove ← {p∈P\{pmin} | p.y > ymid }
T składa się z:
– korzenia v, w nim: p(v)=pmin i y(v)=ymid
– left(v), right(v): drzewa priorytetowe dla Pbelow i
Pabove.
Drzewa priorytetowe
Cel: konstrukcja struktury danych, która dla
• zbioru punktów S
pozwala szybko odpowiadać na zapytania
przedziałowe postaci:
(-∝,qx]×[qy,q’y]
i zajmuje małą (liniową) pamięć.
Wyszukiwanie wg wsp. x
ReportInSubtreeX(v,qx)
Dane: v, korzeń PT (priority tree), qx – liczba.
Wynik: wszystkie p-ty o wsp. x mn. od qx
Jeśli v nie jest liściem i p(v).x < qx:
• Wypisz p(v)
• ReportInSubtreeX(left(v), qx)
• ReportInSubtreeX(right(v), qx)
SearchPrioTree (T, R=(-∝,qx]×[qy,q’y])
Dane: T: drzewo PT; (-∝,qx]×[qy,q’y]: przedział.
Wynik: wszystkie p-ty w prostokącie R
• path1: ścieżka wyszukiwania dla qy (wg pól y(∗
∗))
• path2: ścieżka wyszukiwania dla q’y (wg pól y(∗
∗))
• vsplit: najniższy wspólny element path1 i path2
• Dla każdego v∈path1, poniżej vsplit:
Drzewa priorytetowe: wydajność
• Pamięć: O(n)
• Czas konstrukcji: O(n log n)
• czas odpowiedzi na zapytania
(-∝,qx]×[qy,q’y]: O(log n + k)
– jeśli path1 skierowana w lewo od v:
• ReportInSubtreeX(right(v), qx)
• Dla każdego v∈path2, poniżej vsplit:
– jeśli path2 skierowana w prawo od v:
• ReportInSubtreeX(left(v), qx)
Drzewa segmentowe (segment trees)
Cel (właściwy):
• struktura danych do zadania okienkowania
(zapytania o zakres/prostokąt dla zbioru
odcinków)
Cel pośredni (raz jeszcze):
• Dane: zbiór przedziałów
I={[x1,x’1],...,[xn,x’n]}
• Cel: utworzenie struktury do zapytań o punkt qx:
– odpowiedź to wszystkie przedziały z I
zawierające qx
Drzewa segmentowe
Dane: zbiór przedziałów
I={[x1,x’1],...,[xn,x’n]}
Definicja drzewa segmentowego dla I:
• p1≤… ≤pm: uporządkowany zbiór wszystkich
końców odcinków z I
• przedziały elementarne:
(-∝,p1),[p1,p1], (p1,p2), [p2,p2],…,
(pm-1,pm),[pm,pm],(pm, +∝)
cdn ...
Drzewa segmentowe: definicja c.d.
Drzewo segmentowe – zbalansowane BST:
• liść: reprezentuje przedział elementarny,
• wierzchołek wewnętrzny: reprezentuje przedział sumę przedziałów elementarnych z poddrzewa,
• w każdym wierzchołku przechowujemy:
– Int(v) – przedział odpowiadający wierzchołkowi v,
– I(v)={[x,x’]∈I | Int(v)⊆[x,x’] oraz Int(parent(v))⊄[x,x’]}
Drzewo segmentowe: wyszukiwanie
Dane: v - korzeń drzewa, qx – liczba
Wynik: wszystkie przedziały zawierające qx
QuerySegmentTree(v,qx)
1. Wypisz wszystkie elementy I(v)
2. Jeśli v nie jest liściem:
•
Jeśli qx∈Int(left(v))
•
PAMIĘĆ: O(n log n)
Lemat. Każdy przedział może wystąpić co najwyżej
dwukrotnie w I(∗
∗) na tym samym poziomie.
Drzewo segmentowe: konstrukcja
• Posortuj zbiór {x1,…,xn} ∪ {x’1,…,x’n} i utwórz
przedziały elementarne
• Zbuduj T - zbalansowane BST dla przedziałów
elementarnych i wyznacz bottom-up wartości
Int(v) dla każdego v∈T
• Dla każdego [x:x’] ∈ I
– InsertSegmentTree(root(T), [x:x’])
•
QuerySegmentTree(left(v), qx)
w przeciwnym przypadku
•
QuerySegmentTree(ritht(v), qx)
InsertSegmentTree(v, [x:x’])
Dane: v - korzeń drzewa [w nim wszystkie przedz.el.]
Wynik: [x:x’] wstawione do odpowiednich I(v’)
• Jeśli Int(v) ⊆ [x:x’]:
– dodaj [x:x’] do I(v),
• w przeciwnym przypadku
– Jeśli Int(left(v))∩[x:x’]≠∅:
• InsertSegmentTree(left(v), [x:x’])
– Jeśli Int(ritht(v))∩[x:x’]≠∅:
• InsertSegmentTree(right(v), [x:x’])
Drzewa segmentowe: wydajność
• Pamięć: O(n log n)
• Czas konstrukcji: O(n log n)
• Zapytanie: O(k+log n)
Windowing: nowe drzewa segmentowe
Dane: zbiór odcinków S (zał.: nie przecinają
się)
1. IS: zbiór rzutów odcinków z S na oś OX
2. Zbuduj drzewo segmentowe dla zbioru
odcinków IS, z modyfikacjami:
•
•
Windowing: reprezentacja I(v)
Obserwacja: odcinki w zbiorze I(v)
„rozpinają” obszar odpowiadający Int(v).
Wniosek: odcinki w I(v) można
uporządkować „pionowo” [relacja „nad” i
„pod”]
W efekcie:
T(v) to zbalansowane BST reprezentujące
I(v), wg porządku pionowego.
I(v) reprezentuje odcinki z S, a nie ich rzuty
I(v) dla każdego wierzchołka v
reprezentujemy jako T(v), drzewo binarne
opisane dalej.
Nowe drzewo segm.: wyszukiwanie
Dane: T – nowe drzewo segm.,
U=[qx]×[qy,q’y] – odcinek pionowy
Wynik: zbiór odcinków z T, przecinających U
• wykonaj QuerySegmentTree(T, qx)
• w każdym odwiedzonym węźle v:
a)
b)
c)
d)
nie wypisuj wszystkich elementów T(v)
wyszukaj qy w T(v) [najwyższy poniżej qy]
wyszukaj q’y w T(v) [najniższy powyżej q’y]
wypisz wszystkie odcinki pomiędzy
znalezionymi w (b) i (c)
Nowe drzewo segm.: wydajność
Pamięć: O(n log n)
Czas konstrukcji: O(n log2n) [można O(n logn)
Czas wyszukiwania: O(log2n+k)