¿Existe un algoritmo en línea para calcular la mediana de una secuencia de números si los elementos de la secuencia se pueden agregar o eliminar en cualquier momento?

Mire el algoritmo Q-Digest: q-digest: un algoritmo para calcular cuantiles aproximados en una colección de enteros. Le permite estimar cuantiles arbitrarios (la mediana sería el percentil 50).

Tenga en cuenta que este es un algoritmo con pérdida, diseñado para manejar grandes flujos de datos que no se ajustan a la memoria disponible.

Otra propiedad interesante de este algoritmo es que se pueden fusionar dos instancias. Esto permite el cálculo distribuido de los cuantiles. Un caso de uso imaginario es un trabajo Map-Reduce:

  1. Varias máquinas calculan una estructura de datos de resumen q independientemente uno del otro
  2. estas estructuras de datos se envían a un agregador para que puedan fusionarse en una sola instancia de q-digest
  3. esta instancia agregada representa todo el conjunto de datos y se puede consultar por percentiles

Hay material más interesante y relevante como p-square (El algoritmo de P-Square para el cálculo dinámico de percentiles e histogramas sin almacenar observaciones / Guía del usuario – 1.55.0) o el documento de Munro-Paterson (Selección y clasificación con almacenamiento limitado)

Respuesta del usuario de Quora a ¿Existe un algoritmo en línea para calcular la mediana de una secuencia de números si los elementos de la secuencia se pueden agregar o eliminar en algún momento ?, los árboles balanceados, es lo que haría.

Una implementación estándar de almacenamiento dinámico utilizando un vector requeriría también una asignación del valor del elemento a su posición, para manejar las eliminaciones correctamente (¿un hashmap?). Los montones utilizan menos espacio que los árboles binarios de búsqueda, pero si también necesita un hashmap, puede ser demasiado grande.

Otra idea sería no eliminar elementos de un montón de forma explícita, sino agregarlos a un montón auxiliar. Luego, siempre debe comparar la parte superior de ambos montones (regular y auxiliar) para ver si la parte superior de uno normal todavía está viva. (Podría agregar algunas reglas de recolección de basura, como si el montón auxiliar tiene más del 50% de elementos, luego simplemente clasifique todo y limpie).

Otra idea solo funcionaría para un pequeño dominio de números (digamos: números del 0 al 255): solo construya un árbol binario implícito estático (similar al utilizado en una implementación de montón) de modo que cada hoja contenga el número de ocurrencias particulares número y los nodos internos almacenan sumas parciales (al igual que en la solución de Tomislav Novak). Eso usaría solo 256 * 2 enteros de memoria y consultas O (lg n) (que se pueden implementar de una manera que no requiera si las ramas: D).

Creo que también podría almacenar solo el 1/3 medio de elementos en el árbol, mientras los mantiene a la izquierda y a la derecha en hashmaps. Siempre que la mediana se acerque al límite del árbol, puede volver a calcular todo.

Sin embargo, lo que realmente me interesa es si hay algunos buenos algoritmos de transmisión que usen solo memoria constante, sin introducir errores grandes (probablemente los hay).

Una forma sencilla de hacer esto es aumentar un árbol de búsqueda binaria (equilibrado) almacenando también para cada nodo el tamaño del subárbol enraizado en ese nodo. Esto hace posible realizar todas las operaciones requeridas (insertar, eliminar y consultar el n / 2-ésimo elemento más pequeño) en O (lg n).

Puede consultar http://www.shygypsy.com/tools/bs … para ver una implementación de un árbol AVL que admita esas operaciones.

Se puede adoptar el enfoque (Método basado en montones) mencionado en el siguiente enlace,
http://www.geeksforgeeks.org/arc

El algoritmo utilizado en el enlace anterior no eliminará elementos.

Es bastante fácil agregar nuevos elementos, sin embargo, la eliminación es complicada. El elemento que se eliminará podría estar en el montón izquierdo o derecho, y después de la eliminación necesitamos equilibrar los montones nuevamente.

EDITAR:
Me perdí de notar que es malo. He interpretado como mediana . Perdón por la publicación incorrecta.

Una de las mejores formas de determinar la mediana del flujo de números es usar la combinación de montón máximo y montón mínimo:

lógica: cree el montón máximo de todos los números mínimos y el montón mínimo de todos los números máximos, de modo que cuando se solicite una mediana se encuentren dos casos:
1. el tamaño del montón máximo es mayor que el montón mínimo, entonces el elemento superior del montón máximo contendrá la mediana.
2. el tamaño del montón máximo es igual al tamaño del montón mínimo, entonces la mediana será el promedio de los dos elementos superiores del montón máximo y el montón mínimo.

La parte crítica es cómo determinar o insertar el nuevo elemento para que la diferencia en el número de elementos en el montón mínimo y el montón máximo esté entre cero y 1.

Aquí está el pseudocódigo para insertar el nuevo elemento:

antes de seguir el código, podemos suponer que estamos utilizando la estructura de datos PriorityQueue presente en el paquete Collection en java para representar nuestro montón:

public void insertData(int data){ if(mxHeap.size()==0) mxHeap.insert(data); if(data= 

De esta manera, la complejidad del tiempo total para construir los montones será O (n * lg n)
y encontrar la mediana will O (1)
En consecuencia, la complejidad del tiempo total será O (n * lg n).

Solución basada en el montón. Tomando dos montones montón máximo y montón mínimo

Para los dos primeros elementos, agregue uno más pequeño al maxHeap a la izquierda y uno más grande al minHeap a la derecha.
Luego, procese los datos del flujo uno por uno
Paso 1:
Agregue el siguiente elemento a uno de los montones si el siguiente elemento es más pequeño que maxHeap root, agréguelo a maxHeap, de lo contrario, agréguelo a minHeap

Paso 2:
Balancee los montones (después de este paso los montones estarán balanceados o uno de ellos contendrá 1 elemento más)
Si el número de elementos en uno de los montones es mayor que el otro en más de 1, elimine el elemento raíz del que contiene más elementos y agréguelo al otro

Luego, en cualquier momento puede calcular la mediana de esta manera:

Si los montones contienen elementos iguales;
mediana = (raíz de maxHeap + raíz de minHeap) / 2
Más
mediana = raíz del montón con más elementos

Vaya aquí Mediana en una secuencia de enteros (enteros en ejecución): GeeksforGeeks. Modifíquelo en consecuencia para que se ajuste a su propósito.

El método del montón (usando dos montones minheap y maxheap) no será eficiente si los datos son muy grandes (digamos 2 ^ 32).
para que sea eficiente, podemos mantener un recuento de elementos encontrados, y podemos hacer un depósito de elementos de datos mediante el conteo. Al hacer esto, podemos reducir la memoria ya que necesitamos mantener el recuento de “0” (ya que no son relevantes para calcular la mediana).
Ahora, si el recuento es mayor que el tamaño de la memoria, generaremos un número aleatorio entre 1 yn, e insertaremos el elemento en esa posición en el depósito.

int n = 0; // Cuenta corriente de elementos observados hasta ahora
#define SIZE 10000
depósito int [TAMAÑO];
while (streamHasData ())
{
int x = readNumberFromStream ();
si (n {
depósito [n ++] = x;
}
más
{
int p = aleatorio (++ n); // Elija un número aleatorio 0> = p si (p {
depósito [p] = x;
}
}
}

ahora, después de esto, cualquier percentil tardará O (1) en calcularse. 🙂

Almacene la media y el número de artículos. Para agregar números

nueva media = (media anterior * # de ítems + nuevo número) / (número de ítems + 1)

Restar:

Nueva media = (media anterior * # de elementos – número a eliminar) / (número de elementos -1)

Creo que esta pregunta se puede hacer usando Multi-set. Este enlace: -> ¡Las actualizaciones medianas seguramente te ayudarán!

More Interesting

¿Qué lenguaje, libro o técnica es el mejor punto de partida cuando estás frustrado con tus habilidades de programación y quieres tener una sólida formación en algoritmos y estructuras de datos?

¿Cuál es la diferencia entre consultas DNS iterativas y recursivas?

¿Cuál es la forma más compleja de reducir 1 + 1?

Cómo multiplicar elementos de matriz sin usar bucle

¿Cuánto conocimiento de implementación de algoritmos usan realmente los programadores experimentados?

¿Cómo funciona la detección de vandalismo de Wikipedia?

Cómo aprender a ser bueno al traducir el problema inicial en un problema de coincidencia gráfica bipartita

¿El conocimiento de algoritmos codiciosos a veces influye en la forma de tomar decisiones?

¿Debo aprender C ++ ahora que sé cómo implementar algoritmos básicos de ML en Python, o debería seguir con scikit-learn?

¿Qué es un algoritmo para darme sistemáticamente todas las combinaciones de elementos r de una matriz de elementos K?

¿Cuál es el mejor algoritmo para realizar la extracción de características para el reconocimiento óptico de caracteres?

¿Cuáles son algunos algoritmos interesantes que no tienen implementación conocida hasta la fecha?

¿Cómo resolvemos el problema B, 'Can of Worms', del Chicago Invitational Programming Contest 2013?

¿Qué es importante saber y estudiar para ser un excelente programador? ¿Es importante practicar programación competitiva?

¿Cuál es una explicación simple de por qué BFS bidireccional se ejecuta en [math] \ Theta (\ sqrt {n}) [/ math]?