¿Por qué Collections.sort usa la ordenación combinada en lugar de la ordenación rápida?

101

Sabemos que la clasificación rápida es el algoritmo de clasificación más rápido.

El JDK6 collections.sortutiliza el algoritmo de clasificación por fusión en lugar de la clasificación rápida. Pero Arrays.sort usa un algoritmo de ordenación rápida.

¿Cuál es la razón por la que Collections.sort usa la ordenación combinada en lugar de la ordenación rápida?

MayurB
fuente
3
A menos que pueda conseguir que un autor de JDK responda, todo lo que obtendrá serán conjeturas. No es una pregunta real.
Marqués de Lorne
4
@EJP Buen punto, pero seguramente "No constructivo" es la razón de cierre correcta. Para mí está claro cuál es la pregunta aquí.
Duncan Jones
2
Porque los chicos de Java decidieron hacerlo así. Pregúntales. No se puede obtener una respuesta legítima aquí, creo. Y la clasificación rápida no es la mejor. Es solo lo mejor para uso genérico .
Adam Arold
4
Una suposición: Quicksort no es estable, Mergesort sí lo es. Para las primitivas, una ordenación estable / no estable es irrelevante, para los objetos podría serlo (o al menos, es posible que se presenten errores contra una ordenación inestable).
parsifal
2
@EJP, no hay nada que impida que las intenciones de los autores de JDK sean públicas. Una vez que sea público, no necesitamos que el propio autor responda. De hecho, es posible obtener una respuesta que sea más que adivinatoria incluso sin que responda un autor de JDK.
Pacerier

Respuestas:

187

Muy probable de Josh Bloch § :

Escribí estos métodos, así que supongo que estoy calificado para responder. Es cierto que no existe el mejor algoritmo de clasificación. QuickSort tiene dos deficiencias importantes en comparación con mergesort:

  1. No es estable (como señaló parsifal).

  2. No garantiza el rendimiento de n log n; puede degradarse a un rendimiento cuadrático en entradas patológicas.

La estabilidad no es un problema para los tipos primitivos, ya que no existe una noción de identidad como distinta de la igualdad (de valores). Y se consideró que la posibilidad de comportamiento cuadrático no era un problema en la práctica para la implementación de Bentely y McIlroy (o posteriormente para Dual Pivot Quicksort ), razón por la cual estas variantes de QuickSort se usaron para los tipos primitivos.

La estabilidad es un gran problema al clasificar objetos arbitrarios. Por ejemplo, suponga que tiene objetos que representan mensajes de correo electrónico y los ordena primero por fecha y luego por remitente. Espera que estén ordenados por fecha dentro de cada remitente, pero eso solo será cierto si el orden es estable. Es por eso que elegimos proporcionar una ordenación estable (Merge Sort) para ordenar las referencias a objetos. (Hablando técnicamente, varios ordenamientos estables secuenciales dan como resultado un orden lexicográfico de las claves en el orden inverso de los ordenamientos: el ordenamiento final determina la subclave más significativa).

Es un buen beneficio adicional que Merge Sort garantiza un rendimiento n log n (tiempo) sin importar la entrada. Por supuesto, hay un lado negativo: la clasificación rápida es una clasificación "en el lugar": solo requiere log n espacio externo (para mantener la pila de llamadas). Fusionar, ordenar, por otro lado, requiere O (n) espacio externo. La variante TimSort (introducida en Java SE 6) requiere sustancialmente menos espacio (O (k)) si la matriz de entrada está casi ordenada.

Además, lo siguiente es relevante:

El algoritmo utilizado por java.util.Arrays.sort y (indirectamente) por java.util.Collections.sort para ordenar las referencias a objetos es un "mergesort modificado (en el que se omite la combinación si el elemento más alto en la sublista baja es menor que el elemento más bajo en la sublista alta) ". Es un tipo estable razonablemente rápido que garantiza el rendimiento de O (n log n) y requiere O (n) espacio adicional. En su día (fue escrito en 1997 por Joshua Bloch), fue una buena elección, pero hoy podemos hacerlo mucho mejor.

Desde 2003, el ordenamiento por lista de Python ha utilizado un algoritmo conocido como timsort (en honor a Tim Peters, quien lo escribió). Es un mergesort iterativo, adaptable y estable que requiere muchas menos de n comparaciones log (n) cuando se ejecuta en matrices parcialmente ordenadas, mientras que ofrece un rendimiento comparable al de un mergesort tradicional cuando se ejecuta en matrices aleatorias. Como todos los mergesorts adecuados, timsort es estable y se ejecuta en tiempo O (n log n) (en el peor de los casos). En el peor de los casos, timsort requiere espacio de almacenamiento temporal para n / 2 referencias a objetos; en el mejor de los casos, solo requiere una pequeña cantidad constante de espacio. Compare esto con la implementación actual, que siempre requiere espacio adicional para n referencias de objetos, y supera a n log n solo en listas casi ordenadas.

Timsort se describe en detalle aquí: http://svn.python.org/projects/python/trunk/Objects/listsort.txt .

La implementación original de Tim Peters está escrita en C. Joshua Bloch la portó de C a Java y finalmente probó, comparó y ajustó ampliamente el código resultante. El código resultante es un reemplazo directo de java.util.Arrays.sort. En datos muy ordenados, este código puede ejecutarse hasta 25 veces más rápido que la implementación actual (en la máquina virtual del servidor HotSpot). En datos aleatorios, las velocidades de las implementaciones antiguas y nuevas son comparables. Para listas muy cortas, la nueva implementación es sustancialmente más rápida que la anterior incluso en datos aleatorios (porque evita la copia de datos innecesaria).

Además, consulte ¿Java 7 utiliza Tim Sort para el método Arrays.Sort? .

No hay una sola "mejor" opción. Como sucede con muchas otras cosas, se trata de compensaciones.

NPE
fuente