Jak działa wyszukiwanie najkrótszych ścieżek? 12
Grono opiera się na zasadzie „sześciu podań ręki”. Oznacza to, że odległość pomiędzy dwoma dowolnymi użytkownikami, liczona w „podaniach ręki” jest niewielka. Pomimo tego, że wynik wyszukiwania najkrótszej ścieżki zawiera listę tylko kilku użytkowników, algorytmicznie jest to bardzo skomplikowane zagadnienie.
Gdy Grono miało stosunkowo niewielu użytkowników opracowany został program, który obliczał najkrótszą ścieżkę pomiędzy dwoma osobami. Była to implementacja standardowego algorytmu Dijkstry.
W miarę gdy Grono się rozwijało i rosła liczba jego użytkowników, metoda ta przestała działać wystarczająco szybko. Czas wyszukiwania ścieżek sięgał kilku sekund. Postanowiliśmy to zmienić.
Próba pierwsza: metoda Dijkstry
Algorytm Dijkstry służy do znajdowania najkrótszych połączeń w grafach.
W naszym przypadku użytkowników można rozpatrywać jako węzły, a
połączenia pomiędzy użytkownikami jako krawędzie grafu. Jednak
zauważmy, że struktura grafu sieci społecznej jest specyficzna. Każde
podanie ręki, każda krawędź grafu, ma jednakową wartość, a odległość
pomiędzy dowolnymi użytkownikami (węzłami) jest zawsze krótka i wynosi
co najwyżej kilkanaście podań ręki.
Dzięki tym cechom możemy zawęzić metodę Dijkstry do naszych potrzeb. Złożoność standardowego algorytmu Dijsktry, O(V * log(V) + E), dla naszego przypadku można zmniejszyć do postaci O(V+E). Osiągniemy to implementując kolejkę priorytetową w algorytmie Dijkstry jako tablicę haszującą. Dzięki temu wszystkie niezbędne operacje, takie jak usuwanie węzłów, zmienianie ich wag i wyszukiwanie węzła o najmniejszej wadze, można wykonać w czasie stałym (w przeciwieństwie do standardowej implementacji metody Dijkstry, gdzie jest to czas logarytmiczny).
Próba druga: szybko zakończyć obliczenia
Standardowy algorytm Dijkstry oblicza ścieżkę od węzła początkowego do
wszystkich węzłów w grafie. Odnosząc to do naszego problemu, gdy
szukamy drogi od gronowicza A do B, to komputer oblicza najkrótsze
ścieżki od gronowicza A do wszystkich osób w Gronie. Aczkolwiek jako
rezultat używana jest zaledwie jedna wyliczona droga. Rozsądne wydaje
się przerwanie obliczeń po znalezieniu pierwszej odpowiadającej
ścieżki. Można by się spodziewać, że w ten sposób znacznie poprawimy
wydajność, gdyż nie będziemy wykonywali wielu niepotrzebnych obliczeń.
Niestety w praktyce poprawa wydajności okazała się niewielka. Dzieje się tak dlatego, że rozkład znajomości w gronie jest bardzo specyficzny. Liczba znajomych w kolejnych podaniach ręki wzrasta tak szybko, że w odległości 4 podań ręki od użytkownika znajdują się już praktycznie wszyscy. Tak więc w momencie w którym przerywalibyśmy szukanie, okazuje się że i tak przeszukaliśmy już większość możliwych dróg.
Oto przykładowe dane, pokazujące jak szybko rośnie liczba znajomych w kolejnych odległościach od pewnego użytkownika:
Odległość Liczba Liczba
Węzłów Krawędzi
#00 | 1n 59e
#01 | 59n 4917e
#02 | 3654n 463608e
#03 | 159042n 17669289e
#04 | 557105n 25225499e
#05 | 261623n 564312e
#06 | 25225n 28748e
#07 | 1879n 2104e
#08 | 190n 221e
#09 | 29n 33e
#10 | 4n 6e
#11 | 2n 2e
Próba trzecia: liczymy z obu stron
Kolejną specyficzną cechą Grona jest fakt, że jeżeli ktoś jest moim
znajomym, to ja muszę być również jego znajomym. Możemy powiedzieć, że
graf znajomości nie jest skierowany. Wynika z tego, że jeśli odwrócimy
ścieżkę od użytkownika A do B, to będzie ona poprawną ścieżką od B do A.
Jak już wspomniałem wcześniej, im dalszy poziom znajomości obliczamy, tym więcej danych musimy obrabiać. Załóżmy, że użytkownika A dzielą cztery podania ręki do użytkownika B. Nie musimy liczyć kosztownego czwartego poziomu znajomości od użytkownika A i w wynikach szukać B. Możemy policzyć wszystkie węzły w odległości 2 od A, wszystkie węzły w odległości 2 od B, a następnie porównać wyniki. Jeśli jakiś użytkownik jest w odległości 2 od A, i zarazem jest w odległości 2 od B, to znaczy że przez niego przebiega szukana ścieżka pomiędzy A i B.
Stosując tą metodę jesteśmy w stanie znacznie zmniejszyć obliczania, gdyż nie musimy obrabiać danych w odległości 3 i więcej od węzła A.
Próba czwarta
Wyniki opisanej metody są bardzo dobre. Jednak w pewnych przypadkach obliczenia w dalszym ciągu trwały długo.
Powyższa metoda wykonuje po jednym kroku algorytmu od węzła A i od węzła B, a następnie porównuje wyniki. Zamiast wykonywać po jednym kroku z obu stron, należy tak zmodyfikować program, aby wykonywał obliczenia tylko po tej stronie ścieżki, po której mamy mniej węzłów. Dzięki temu mamy pewność, że wykonujemy możliwie mało obliczeń. Niestety, odbywa się to kosztem dwukrotnie większej liczby scaleń rezultatów obliczeń.
Podsumowanie
Po uwzględnieniu specyficznych cech struktury Grona, udało się znacznie
zmodyfikować algorytm Dijkstry. Dzięki temu czas wyszukiwania
najkrótszych ścieżek pomiędzy Gronowiczami spadł z kilku sekund do
kilku milisekund.
Jaka jest złożoność przedstawionej metody? (autor: Adam Kolipiński, plazmonik (małpa) gmail.com)
TWIERDZENIE: Weźmy G: graf nieskierowany, o wagach krawędzi równych 1, o nieskończonej liczbie wierzchołków, spójny, taki, że odległość między dowolnymi wierzchołkami jest skończona, oraz taki, że z każdego wierzchołka wychodzi co najwyżej K krawędzi. Załóżmy że odległość między wierzchołkami A i B wynosi N.
Pesymistyczny koszt algorytmu Dijktry wynosi O(KN ), a koszt algorytmu liczącego z dwóch stron: O(KN/2) + koszt scalania tablic.
DOWÓD: Algorytm Dijkstry potrzebuje zbadać K wierzchołków aby dojść do wierzchołka o odległości 1, K2 wierzchołków, aby dojść do wierzchołka o odległości 2, itd, przy założeniu najgorszego przypadku, a więc za każdym razem trafiamy na nieodwiedzony wcześniej wierzchołek.
To daje nam sumę ciągu geometrycznego:K(Dijk) = k1 + k2 + .... + kn = (1-kn)/(1-k) ~ kn
Algorytm liczący z dwóch stron bada 2K wierzchołków aby dojść do wierzchołka o odległości 1 i 2, 2K2, aby dojść do wierzchołka o odległości 3 i 4, itd. co daje sumę:K(DStron) = 2 * (k1 + k2 + ... + k n/2) + n = 2 * (1 – kn/2)/(1 – k) + n ~ kn/2
Przy każdym kroku algorytmu trzeba scalić tablicę, stąd to ‘+n’.

Genialne, genialne!!!!
Nie mam bladego pojęcia o co chodzi…...
Homer Simpson
swietnie, byc moze to rozwiazanie jest wystarczajace… tylko ze istnieje prostsze rozwiazanie. wlasnie spedzilem 15 min nad kartka papieru i wymyslilem algorytm, ktory moze obliczac najkrotsze sciezki w tym specyficznym przypadku grona w duzo prostszy sposob :)
ja też nic z tego nie kapuje ale wiem jedno dzisiaj trzykrotnie próbowałem skorzystać z wyszukiwania najkrótszej ścieżki i raz poszło gładko a dwa razy otrzymałem komunikat “Serwer ścieżek aktualnie się odświeża, proszę spróbować za 5 minut.” Więcej razy już mi się nie chce próbować.
Dane co jakiś czas muszą się odświeżyć, żeby nie pokazywać Ci błędnych wyników. Najkrótsze ścieżki to fajna sprawa – nie zniechęcaj się przez bunt maszyn ;)
Pozdrawiam, Mikołaj N. Grono
nie rozumiem: skoro jak pisze Marek Majkowski “czas wyszukiwania najkrótszych ścieżek pomiędzy Gronowiczami spadł z kilku sekund do kilku milisekund”, to dlaczego Mikołaj N. pisze “Dane co jakiś czas muszą się odświeżyć”?
jeśli rzeczywiście trwa to kilka milisekund, to ścieżki mogą się bez problemu generowac w chwili wejścia na odpowiednią stronę. A tymczasem mamy ciągle to “odświeżanie” i “prosze wrócić za 5 minut”.
Nie wierzę w te milisekundy :)
@m: może podzielisz się z gronem swoim algorytmem? hę?
połowa serwerów działa na nowych kodzie, połowa jeszcze na starym
Wszystkie serwery ścieżek są pracują już na nowym mechanizmie.
Odświeżyć sie muszą znajomości – tj. krawedzie. Zmiany pewnie nie sa na biezaco.
A co do wyszukiwania sciezek – to na poczatku w gronie rzeczywiscie o to chodzilo ;) a potem, to juz wszyscy sobie huby znajomych robia i jest to bez sensu ;) “Znamy sie?” “Nie, chce miec wiecej znajomych!” Super ;-)
ale powiedzcie, zadzialalo wam to kiedys? u mnie ciagle jest, zeby sprobowac za 5 minut. :P
o, teraz wreszcie dziala poprawnie :>
gawd, długo nad tym myśleliście? ;)
chyba bardzo potrzebujecie kogoś, kto się zna na programowaniu, a nie generowaniu kodu.
a w skrócie nazywa się to Bidirectional search ( http://en.wikipedia.org/wiki/Bidirectional_search ), czyli dwustronne przeszukiwanie wszerz i z algorytmem Dijkstry nie ma za wiele wspólnego, problem w tym że na badaniach operacyjnych uczą Dijkstry, a zapominają o o wiele prostszych (i szybszych) algorytmach. Stąd to ponowne odkrywanie Ameryki – “pierwszą próbę” można nazwać “usunięciem Algorytmu Dijkstry” ;)