Sztuczka jest następująca: Otrzymujesz aktualizacje w losowych czasach poprzez void update(int time, float value)
. Jednak musisz również śledzić, kiedy aktualizacja wypadnie z okna czasowego, co oznacza, więc ustawiasz "alarm", który wywołał na time + N
, który usuwa aktualizację poprzednią od kiedykolwiek rozważaną ponownie w obliczeniach.
Jeśli to dzieje się w czasie rzeczywistym, można zwrócić się do systemu operacyjnego, aby nawiązać połączenie ze sposobem void drop_off_oldest_update(int time)
być nazywany w time + N
Jeśli jest to symulacja, nie można uzyskać pomoc z systemu operacyjnego i trzeba to zrobić ręcznie. W symulacji można wywołać metody z czasem podanym jako argument (który nie koreluje z czasem rzeczywistym). Jednak rozsądnym założeniem jest to, że połączenia są gwarantowane tak, że argumenty czasu rosną. W takim przypadku musisz zachować posortowaną listę wartości czasu alarmu, a dla każdego połączenia update
i read
sprawdzić, czy argument czasu jest większy niż nagłówek listy alarmów.Podczas gdy jest on większy, wykonujesz przetwarzanie związane z alarmem (usuwając najstarszą aktualizację), usuń głowicę i sprawdź ponownie, aż wszystkie alarmy przed danym czasem zostaną przetworzone. Następnie wykonaj wywołanie aktualizacji.
Do tej pory zakładałem, że jest oczywiste, co zrobiłbyś dla rzeczywistych obliczeń, ale rozwiążę je na wszelki wypadek. Zakładam, że masz metodę float read (int time)
, której używasz do odczytu wartości. Celem jest uczynienie tego połączenia tak efektywnym, jak to tylko możliwe. Więc obliczasz średnią ruchomą za każdym razem, gdy wywoływana jest metoda read
. Zamiast tego należy wstępnie obliczyć wartość z ostatniej aktualizacji lub ostatniego alarmu i "poprawić" tę wartość przez kilka operacji zmiennoprzecinkowych, aby uwzględnić upływ czasu od ostatniej aktualizacji. (i. e. stałą liczbę operacji, z wyjątkiem prawdopodobnie przetwarzania listy alarmów ułożonych w stos).
Mam nadzieję, że jest to jasne - powinien to być dość prosty algorytm i dość wydajny.
Dalsza optymalizacja: jeden z pozostałych problemów jest jeśli duża liczba aktualizacji zdarzyć w oknie czasowym, to nie jest to długi czas, dla których nie są ani czyta ani aktualizacje, a następnie odczytu lub zmiana przychodzi . W takim przypadku powyższy algorytm będzie nieskuteczny przy stopniowej aktualizacji wartości każdej z aktualizacji, która spada. Nie jest to konieczne, ponieważ dbamy tylko o ostatnią aktualizację poza przedziałem czasowym, więc jeśli istnieje sposób, aby skutecznie usunąć wszystkie starsze aktualizacje, pomogłoby to.
Aby to zrobić, możemy zmodyfikować algorytm do binarnego wyszukiwania aktualizacji, aby znaleźć najnowszą aktualizację przed upływem okna czasowego. Jeśli istnieje stosunkowo niewiele aktualizacji, które należy "upuścić", wówczas można aktualizować przyrostowo wartość dla każdej usuniętej aktualizacji. Ale jeśli istnieje wiele aktualizacji, które należy usunąć, można ponownie obliczać wartość od zera po usunięciu starych aktualizacji.
Dodatek bieżących obliczeń: powinienem wyjaśnić, co mam na myśli bieżących obliczeń powyżej w zdaniu „dostrojenia” tę wartość przez kilka operacji zmiennoprzecinkowych, aby uwzględnić upływ czasu od ostatniej aktualizacji. Początkowa bez przyrostowego Obliczenia:
Rozpocznij
sum = 0;
updates_in_window = /* set of all updates within window */;
prior_update' = /* most recent update prior to window with timestamp tweaked to window beginning */;
relevant_updates = /* union of prior_update' and updates_in_window */,
następnie iteracyjnego relevant_updates
w kolejności od czasu:
for each update EXCEPT last {
sum += update.value * time_to_next_update;
},
i wreszcie
moving_average = (sum + last_update * time_since_last_update)/window_length;
.
Teraz, jeśli dokładnie jedna zmiana spada z okna, ale żadne nowe aktualizacje przybyć dostosować sum
jak:
sum -= prior_update'.value * time_to_next_update + first_update_in_last_window.value * time_from_first_update_to_new_window_beginning;
(pamiętać, że jest prior_update'
która jego timestamp zmodyfikowany zacząć od ostatniego okna rozpoczynającego). A jeśli dokładnie jedna zmiana wejdzie przez okno, lecz żadne nowe aktualizacje odpadać, wyregulować sum
jak:
sum += previously_most_recent_update.value * corresponding_time_to_next_update.
Jak powinno być oczywiste, to jest szorstki szkic, ale mam nadzieję, że to pokazuje, w jaki sposób można utrzymać średnią tak, że jest O (1) operacje na aktualizację według zamortyzowanego kosztu. Ale zanotuj dalszą optymalizację w poprzednim paragrafie.Należy również zwrócić uwagę na problemy związane ze stabilnością, do których nawiązuje starsza odpowiedź, co oznacza, że błędy zmiennoprzecinkowe mogą kumulować się w dużej liczbie takich operacji przyrostowych, tak że istnieje rozbieżność w wyniku pełnego obliczenia, który jest istotny dla aplikacji.
Co masz do tej pory? Skąd wiesz, że jest to nieefektywne? –
Interesujące pytanie, ale będąc oznaczonym jako C++ Spodziewam się zobaczyć kod, który posiadasz. W tej chwili mogę tylko powiedzieć, że musisz znaleźć sposób na interpolację między danymi w danych wejściowych i oprzeć algorytm na danym oknie czasowym i liczbie próbek. – sehe
Może to być przydatne w twoim kontekście, ale * średnia krocząca * może być alternatywą dla okna o stałym oknie. Obliczanie rekurencyjne jest bardzo łatwe. – NPE