Co to jest?
Migawki informatyczne
Algorytmy strumieniowe
W dzisiejszym świecie cyfrowym mamy do czynienia z olbrzymią ilością danych, wielu Czytelników słyszało zapewne modne ostatnio hasło "Big Data". I trzeba sobie z tym radzić, a problemy mogą pojawiać się w nieoczekiwanych miejscach. Przyzwyczajeni jesteśmy do myślenia, że programy mają pewne dane na wejściu i te dane są tam na stałe, program może je w dowolnym momencie przeczytać. Czasami jednak nie do końca przystaje to do rzeczywistości...
Wyobraźmy sobie, że analizujemy kamerę online, która transmituje obraz 24 godziny na dobę i chcemy po miesiącu wydobyć pewne statystyki z tego, co nagrała. Możemy, oczywiście, zapisać całość na dysk i potem obliczyć to, co trzeba. Łatwo sobie jednak wyobrazić, że potrzeba na to wiele pamięci, którą niekoniecznie zamierzamy właśnie na to przeznaczać. Dobrze byłoby na bieżąco obliczać to, co chcemy, i nie zapisywać całego filmu - pamiętać tylko statystyki z przeszłości i uaktualniać je w czasie rzeczywistym. Podobne problemy spotykamy w wielu miejscach, gdy rozważamy duży strumień danych, przykładowo film lub dźwięk, ale również inne, jak ruch pakietów TCP/IP w sieci. Taka jest geneza powstania dziedziny algorytmów strumieniowych, czyli działających na strumieniu danych i obliczających na bieżąco pewne jego własności. Jej matematyczne podstawy są, moim zdaniem, wyjątkowo piękne.
Założymy, że nasz strumień danych to ciąg dla pewnego dużego oraz że dla każdego Niech będzie liczbą wystąpień liczby w całym strumieniu. Naszym celem będzie obliczenie pewnych własności strumienia w pamięci istotnie mniejszej niż
Na początek przyjrzyjmy się następującej zagadce. Rozważmy strumień, w którym pewna wartość występuje więcej niż na połowie miejsc. Czyli istnieje takie, że Jak wtedy znaleźć Nie jest jasne, jak to zrobić bez trzymania aktualnych wartości dla wszystkich podczas przeglądania strumienia. Okazuje się, że działa algorytm naprawdę banalny. W każdej chwili pamiętamy parę liczb. Pierwsza liczba to kandydat na odpowiedź, a druga liczba to coś jakby jego przewaga nad resztą. Inicjalizujemy parę jako Powiedzmy, że po przeczytaniu pamiętamy parę Są dwie możliwości. Jeśli to po przeczytaniu ustawiamy parę na Jeśli natomiast to rozważamy dwa przypadki. Jeżeli to zwiększamy przewagę, czyli ustawiamy Jeśli natomiast to ustawiamy Na końcu zwracamy pierwszy element z pary. Zauważmy jednak, że algorytm wcale nie trzyma prawdziwego dotychczasowego rekordzisty wraz z jego przewagą. Po przeczytaniu pierwszych sześciu elementów ciągu będzie trzymał parę a więc po przeczytaniu ostatniego wyrazu parę Dlaczego więc działa poprawnie? Przypomnijmy, że pewien element występuje na więcej niż połowie miejsc. Niech wartość pary będzie równa o ile oraz o ile Zauważmy, że napotkanie elementu zawsze zwiększa o jeden wartość pary, a innego niż - zmniejsza ją lub zwiększa o jeden. Na początku wartość pary to Zatem po przeczytaniu całego ciągu wartość pary będzie dodatnia, czyli pierwszy jej element to faktycznie
Ważnej informacji o własnościach strumienia dostarczają liczby zdefiniowane jako Dla to liczba różnych elementów, dla to po prostu a dla jest to ważna miara równomierności występowania wartości w ciągu. Bardzo ładny i elegancki jest przybliżony algorytm liczenia wartości w pamięci jedynie My najpierw pokażemy, jak to zrobić w pamięci co nie jest takie złe, bo to zwykle jest olbrzymie, a może być niewielkie. Algorytm używa losowości. Dla każdego losujemy wartość tak, że oraz losowania są niezależne. Na początek ustalamy licznik na 0 i zaczynamy przeglądać strumień. Gdy czytamy element to zwiększamy licznik o Na końcu zwracamy twierdząc, że jest to dobre przybliżenie Algorytm prosty, ale dlaczego to w ogóle ma działać? Najpierw zauważmy, że każde zwiększyło wartość o czyli w sumie wszystkie zwiększyły go o A więc na końcu Obliczmy wartość oczekiwaną Mamy
Zauważmy, że o ile tylko bo oraz są niezależne. A zatem
I ten fakt wydaje się najciekawszy. Wypadałoby jeszcze wykazać, że błąd tego obliczenia (czyli wariancja) jest nie za duży. My tutaj jedynie podamy na wiarę, że Ambitnych Czytelników zachęcamy do przerachowania. Błąd możemy łatwo zmniejszyć, uruchamiając takich samych algorytmów równocześnie, a na końcu uśredniając ich wyniki rezultat oznaczmy Wówczas wartość oczekiwana się nie zmieni, a wariancja zmaleje 1000 razy. Przypomnijmy nierówność Czebyszewa: dla dowolnego Dostajemy więc czyli wynik wychodzi całkiem niezły.
Przypomnijmy, że nasz algorytm działa w pamięci a jednak czasem może być duże. Liczba bierze się stąd, że przez cały czas przeglądania strumienia pamiętamy zmiennych Wydaje się to nieuniknione przy tej technice. Okazuje się jednak, że stosunkowo łatwo można zamienić na używając bardzo pomysłowej metody. Przypomnijmy, jaka własność zmiennych była nam potrzebna. Mianowicie chcieliśmy, by dla czyli by były one parami niezależne. Przy szacowaniu wariancji przydaje się ponadto niezależność czwórkami, dzięki której wiele ze składników postaci znika. Można skonstruować obiekt wielkości z którego można wydobyć zmiennych czwórkami niezależnych. My pokażemy jedynie, że da się tak zrobić dla zmiennych parami niezależnych. Zachęcamy Ambitnego Czytelnika do uogólnienia tego faktu. Dla uproszczenia załóżmy, że w tym przypadku mamy Wówczas pamiętamy niezależnych zmiennych losowych takich, że Dla dowolnego niepustego podzbioru definiujemy zmienną Łatwo sprawdzić, że i istotnie są niezależne dla dwóch różnych zbiorów i
Algorytm obliczający przybliżoną wartość został opublikowany w 1999 roku przez Alona (patrz też str. 8), Matiasa i Szegedego jako chyba najbardziej zaskakujący wynik ich przełomowej pracy. Praca ta położyła fundamenty pod bardzo szybko rosnącą dziedzinę algorytmów strumieniowych, a autorzy otrzymali prestiżową nagrodę Gödla.