El truco es el siguiente: Recibe actualizaciones en momentos aleatorios a través de void update(int time, float value)
. Sin embargo, también es necesario que también realice un seguimiento cuando una actualización caiga la ventana de tiempo, por lo que establece una "alarma" que llamó al time + N
que elimina la anterior actualización que nunca se considerará nuevamente en el cálculo.
Si esto ocurre en tiempo real, puede solicitar que el sistema operativo para realizar una llamada a un método void drop_off_oldest_update(int time)
ser llamado a time + N
Si esto es una simulación, no se puede obtener ayuda del sistema operativo y se necesito hacerlo manualmente En una simulación, llamaría a los métodos con el tiempo suministrado como argumento (que no se correlaciona con el tiempo real). Sin embargo, una suposición razonable es que las llamadas están garantizadas de tal manera que los argumentos de tiempo están aumentando. En este caso, necesita mantener una lista ordenada de valores de tiempo de alarma, y para cada llamada update
y read
, compruebe si el argumento de tiempo es mayor que el encabezado de la lista de alarmas.Si bien es mayor, usted realiza el procesamiento relacionado con la alarma (deje la actualización más antigua), retire el cabezal y vuelva a verificar hasta que se procesen todas las alarmas anteriores al tiempo determinado. Luego haz la llamada de actualización.
Hasta ahora he supuesto que es obvio lo que harías para el cálculo real, pero lo elaboraré por las dudas. Supongo que tiene un método float read (int time)
que usa para leer los valores. El objetivo es hacer que esta llamada sea lo más eficiente posible. Por lo tanto, haga no calcule el promedio móvil cada vez que se llame al método read
. En su lugar, calcula previamente el valor a partir de la última actualización o la última alarma, y "ajusta" este valor mediante un par de operaciones de punto flotante para tener en cuenta el paso del tiempo desde la última actualización. (Es decir, un número constante de operaciones, excepto tal vez para procesar una lista de alarmas acumuladas).
Afortunadamente esto es claro, este debería ser un algoritmo bastante simple y bastante eficiente.
Optimización adicional: uno de los problemas restantes es que si se produce una gran cantidad de actualizaciones dentro de la ventana de tiempo, hay un tiempo largo para el que no hay ni actualizaciones ni actualizaciones, y luego aparece una lectura o actualización . En este caso, el algoritmo anterior será ineficaz para actualizar incrementalmente el valor de cada una de las actualizaciones que se está cayendo. Esto no es necesario porque solo nos preocupa la última actualización más allá de la ventana de tiempo, por lo que si hay una forma de dejar de forma eficiente todas las actualizaciones anteriores, sería útil.
Para hacer esto, podemos modificar el algoritmo para hacer una búsqueda binaria de actualizaciones para encontrar la actualización más reciente antes de la ventana de tiempo. Si hay relativamente pocas actualizaciones que se deben "descartar", entonces se puede actualizar incrementalmente el valor de cada actualización eliminada. Pero si hay muchas actualizaciones que se deben descartar, se puede volver a calcular el valor desde cero después de dejar las actualizaciones anteriores.
Apéndice sobre incremental Cálculo: que debe aclarar lo que quiero decir por el cómputo incremental de más arriba en la sentencia "ajustar" este valor por un par de operaciones de punto flotante para dar cuenta del paso del tiempo desde la última actualización. inicial no incremental cálculo:
comienzo con
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 */,
continuación, iterar sobre relevant_updates
con el fin de aumentar el tiempo:
for each update EXCEPT last {
sum += update.value * time_to_next_update;
},
y finalmente
moving_average = (sum + last_update * time_since_last_update)/window_length;
.
Ahora bien, si exactamente una actualización cae de la ventana, pero no hay nuevas actualizaciones llegan, ajustar sum
como:
sum -= prior_update'.value * time_to_next_update + first_update_in_last_window.value * time_from_first_update_to_new_window_beginning;
(tenga en cuenta que es prior_update'
que tiene su marca de tiempo modificado al inicio de la última ventana de inicio). Y si exactamente una actualización entra en la ventana, pero no hay nuevas actualizaciones se caen, ajuste sum
como:
sum += previously_most_recent_update.value * corresponding_time_to_next_update.
Como debería ser obvio, esto es un boceto pero esperemos que muestra cómo se puede mantener el promedio tal que es O (1) operaciones por actualización sobre una base amortizada. Pero tenga en cuenta una mayor optimización en el párrafo anterior.También tenga en cuenta los problemas de estabilidad aludidos en una respuesta anterior, lo que significa que los errores de punto flotante pueden acumularse en un gran número de tales operaciones incrementales de modo que haya una divergencia con respecto al resultado del cómputo completo que es significativo para la aplicación.
¿Qué tienes hasta ahora? ¿Cómo sabes que es ineficiente? –
Pregunta interesante, pero al ser etiquetada C++, espero ver el código que tienes. En este momento, todo lo que puedo decir es que debe encontrar una manera de interpolar entre los puntos de datos dados en la entrada, y basar su algoritmo en una ventana de tiempo determinada y el número de muestras. – sehe
Esto puede o no ser útil en su contexto, pero una media móvil * exponencial * podría ser una alternativa adecuada a una ventana fija. Es muy fácil de calcular recursivamente. – NPE