Necesito calcular la mediana de carrera:
Entrada: , , vector .
Salida: vector , donde es la mediana de .
(No hacer trampa con aproximaciones; me gustaría tener soluciones exactas. Los elementos son enteros grandes).
Hay un algoritmo trivial que mantiene un árbol de búsqueda de tamaño ; el tiempo total de ejecución es . (Aquí, un "árbol de búsqueda" se refiere a una estructura de datos eficiente que admite inserciones, eliminaciones y consultas medianas en tiempo logarítmico).
Sin embargo, esto me parece un poco estúpido. Aprenderemos efectivamente todas las estadísticas de pedidos dentro de todas las ventanas de tamaño , no solo las medianas. Además, esto no es demasiado atractivo en la práctica, especialmente si es grande (los árboles de búsqueda grandes tienden a ser lentos, la sobrecarga en el consumo de memoria no es trivial, la eficiencia de la memoria caché a menudo es pobre, etc.).
¿Podemos hacer algo sustancialmente mejor?
¿Hay límites inferiores (por ejemplo, ¿es el algoritmo trivial asintóticamente óptimo para el modelo de comparación)?
Editar: David Eppstein dio un buen límite inferior para el modelo de comparación. Me pregunto si, sin embargo, es posible hacer algo un poco más inteligente que el algoritmo trivial.
Por ejemplo, podríamos hacer algo en este sentido: dividir el vector de entrada en partes de tamaño ; ordenar cada parte (haciendo un seguimiento de las posiciones originales de cada elemento); y luego use el vector ordenado por partes para encontrar las medianas en ejecución de manera eficiente sin ninguna estructura de datos auxiliar. Por supuesto, esto seguiría siendo , pero en la práctica la ordenación de matrices tiende a ser mucho más rápida que el mantenimiento de los árboles de búsqueda.
Edición 2: Saeed quería ver algunas razones por las que creo que la ordenación es más rápida que las operaciones del árbol de búsqueda. Aquí hay puntos de referencia muy rápidos, para , :
- ≈ 8s: ordenando vectores con elementos cada uno
- ≈ 10s: ordenando un vector con elementos
- ≈80: inserciones y eliminaciones en una tabla hash de tamaño k
- ≈ 390s: inserciones y eliminaciones en un árbol de búsqueda equilibrado de tamaño k
La tabla hash está ahí solo para comparar; no es de uso directo en esta aplicación.
En resumen, tenemos casi un factor 50 de diferencia en el rendimiento de la ordenación frente a las operaciones de árbol de búsqueda equilibrado. Y las cosas empeoran mucho si aumentamos .
(Detalles técnicos: Datos = enteros aleatorios de 32 bits. Computadora = una computadora portátil moderna típica. El código de prueba se escribió en C ++, utilizando las rutinas de biblioteca estándar (std :: sort) y las estructuras de datos (std :: multiset, std :: unsorted_multiset). Utilicé dos compiladores C ++ diferentes (GCC y Clang), y dos implementaciones diferentes de la biblioteca estándar (libstdc ++ y libc ++). Tradicionalmente, std :: multiset se ha implementado como un árbol rojo-negro altamente optimizado).
fuente
Respuestas:
Aquí hay un límite inferior de la clasificación. Dado un conjunto de entrada de longitud n que se ordenará, cree una entrada para su problema medio de ejecución que consiste en n - 1 copias de un número menor que el mínimo de S , luego S en sí, luego n - 1 copias de un número mayor que el máximo de S , y establezca k = 2 n - 1 . Las medianas de funcionamiento de esta entrada son el mismo que el orden de clasificación de S .S norte n - 1 S S n - 1 S k = 2 n - 1 S
Entonces, en un modelo de cálculo de comparación, se requiere el tiempo . Posiblemente, si sus entradas son enteras y utiliza algoritmos de clasificación de enteros, puede hacerlo mejor.Ω ( n logn )
fuente
Editar: este algoritmo ahora se presenta aquí: http://arxiv.org/abs/1406.1717
Sí, para resolver este problema es suficiente realizar las siguientes operaciones:
A grandes rasgos, la idea es esta:
Para cada :yo
Las listas vinculadas son solo matrices de elementos de índices, por lo que son livianas (excepto que la localidad de acceso a la memoria es deficiente).k
Aquí hay una implementación de muestra y puntos de referencia:
Aquí hay una gráfica de tiempos de ejecución (para ):n ≈ 2 ⋅ 106 6
fuente
Dado el límite de David, es poco probable que pueda hacerlo mejor en el peor de los casos, pero hay mejores algoritmos sensibles a la salida. Específicamente, si en el número de medianas en el resultado, podemos resolver el problema en el tiempo O ( n log m + m log n ) .metro O ( n logm + m logn )
Para hacer esto, reemplace el árbol binario balanceado con un árbol binario balanceado que consista solo de aquellos elementos que fueron medianas en el pasado, más dos montones de Fibonacci entre cada par de medianas anteriores (una para cada dirección), más recuentos para que podamos localice qué montón de Fibonacci contiene un elemento particular en el orden. No te molestes en eliminar elementos. Cuando insertamos un nuevo elemento, podemos actualizar nuestra estructura de datos en tiempo . Si los nuevos recuentos indican que la mediana está en uno de los montones de Fibonacci, se necesita una O adicional ( log n ) para extraer la nueva mediana. Este O ( log n )O ( logm ) O ( logn ) O ( logn ) la carga se produce solo una vez por mediana.
Si hubiera una forma limpia de eliminar elementos sin dañar la agradable complejidad del montón de Fibonacci, llegaríamos a , pero no estoy seguro de si esto es posible.O ( n logm + m logk )
fuente