¿Por qué es quicksort mejor que otros algoritmos de clasificación en la práctica?

31

Este es un reenvío de una pregunta sobre cs.SE de Janoma . Créditos completos y botín para él o cs.SE.

En un curso de algoritmos estándar se nos enseña que quicksort es O (n log n) en promedio y O (n²) en el peor de los casos. Al mismo tiempo, se estudian otros algoritmos de clasificación que son O (n log n) en el peor de los casos (como mergesort y heapsort ), e incluso tiempo lineal en el mejor de los casos (como bubbleort ) pero con algunas necesidades adicionales de memoria.

Después de un rápido vistazo a algunos tiempos de ejecución más , es natural decir que quicksort no debería ser tan eficiente como otros.

Además, tenga en cuenta que los estudiantes aprenden en los cursos de programación básica que la recursividad no es realmente buena en general porque podría usar demasiada memoria, etc. Por lo tanto (y aunque esto no es un argumento real), esto da la idea de que la clasificación rápida podría no ser realmente bueno porque es un algoritmo recursivo.

¿Por qué, entonces, la clasificación rápida supera a otros algoritmos de clasificación en la práctica? ¿Tiene que ver con la estructura de los datos del mundo real ? ¿Tiene que ver con la forma en que funciona la memoria en las computadoras? Sé que algunos recuerdos son mucho más rápidos que otros, pero no sé si esa es la verdadera razón de este rendimiento contraintuitivo (en comparación con las estimaciones teóricas).

Rafael
fuente
3
La reputación de Quicksort data de una época en la que no existía el caché.
Programador
99
"¿Por qué la clasificación rápida supera a otros algoritmos de clasificación en la práctica?" Claro que es verdad? Muéstranos la implementación real a la que te refieres con esta declaración, y la comunidad te dirá por qué esa implementación específica se comporta de la manera que lo hace. Todo lo demás conducirá a suposiciones sobre programas inexistentes.
Doc Brown
1
@DocBrown: muchas implementaciones de Quicksort (o variantes de la misma) se eligen en muchas bibliotecas, posiblemente porque funcionan mejor (eso espero). Por lo tanto, podría haber algo en el algoritmo que acelere Quicksort, independientemente de la implementación .
Raphael
1
Alguien tiene que decir esto para completar, así que lo haré: Quicksort no es (generalmente) estable. Por esta razón, es posible que no desee usarlo. Además, por esta razón, su clasificación predeterminada puede no ser un Quicksort incluso cuando eso es lo que desea.
RalphChapin
1
@Raphael: A menudo, lo que se denomina ordenación rápida es en realidad alguna variación, como la ordenación de introducción (utilizada, afaik, en la biblioteca estándar de C ++), no la ordenación rápida pura.
Giorgio

Respuestas:

21

No estaría de acuerdo con que quicksort sea mejor que otros algoritmos de clasificación en la práctica.

Para la mayoría de los propósitos, Timsort : el híbrido entre clasificación de fusión / inserción que explota el hecho de que los datos que clasifica a menudo comienzan casi ordenados o ordenados de forma inversa.

El ordenamiento rápido más simple (sin pivote aleatorio) trata este caso potencialmente común como O (N ^ 2) (reduciéndose a O (N lg N) con pivotes aleatorios), mientras que TimSort puede manejar estos casos en O (N).

De acuerdo con estos puntos de referencia en C # que comparan el incorporado en la clasificación rápida a TimSort, Timsort es significativamente más rápido en los casos en su mayoría ordenados, y ligeramente más rápido en el caso de datos al azar y TimSort consigue mejor si la función de comparación es particularmente lento. No he repetido estos puntos de referencia y no me sorprendería si quicksort supera ligeramente a TimSort por alguna combinación de datos aleatorios o si hay algo peculiar en el ordenamiento integrado de C # (basado en quicksort) que lo está ralentizando. Sin embargo, TimSort tiene distintas ventajas cuando los datos pueden clasificarse parcialmente, y es aproximadamente igual a la clasificación rápida en términos de velocidad cuando los datos no se clasifican parcialmente.

TimSort también tiene una ventaja adicional de ser un tipo estable, a diferencia de quicksort. La única desventaja de TimSort utiliza la memoria O (N) versus O (lg N) en la implementación habitual (rápida).

dr jimbob
fuente
18

La ordenación rápida se considera más rápida porque el coeficiente es más pequeño que cualquier otro algoritmo conocido. No hay razón o prueba para eso, simplemente no se ha encontrado un algoritmo con un coeficiente más pequeño. Es cierto que otros algoritmos también tienen tiempo O ( n log n ), pero en el mundo real el coeficiente también es importante.

Tenga en cuenta que para la inserción de datos pequeños, el tipo (el que se considera O ( n 2 )) es más rápido debido a la naturaleza de las funciones matemáticas. Esto depende de los coeficientes específicos que varían de una máquina a otra. (Al final, solo se está ejecutando realmente el ensamblaje). Entonces, a veces, un híbrido de clasificación rápida e inserción es lo más rápido en la práctica, creo.

Ramzi Kahil
fuente
77
+ Derecha Los maestros deben ser más conscientes (y yo era maestro) del hecho de que los factores constantes pueden variar según el orden de magnitud. Entonces, la habilidad de ajuste de rendimiento realmente importa, independientemente de big-O. El problema es que siguen enseñando gprof , solo porque tienen que superar ese punto en el plan de estudios, que es 180 grados el enfoque equivocado.
Mike Dunlavey
2
“No hay razón o pro [o] f para eso”: seguro que la hay. Si cavas lo suficientemente profundo, encontrarás una razón.
Gilles 'SO- deja de ser malvado'
2
@B Seven: para simplificar mucho ... para un algoritmo de ordenación O (n log n), hay (n log n) iteraciones del ciclo de ordenación para ordenar n elementos. El coeficiente es la duración de cada ciclo del ciclo. Cuando n es realmente grande (al menos miles), el coeficiente no importa tanto como O () incluso si el coeficiente es enorme. Pero cuando n es pequeño, el coeficiente importa, y puede ser lo más importante si solo está ordenando 10 elementos.
Matt Gallagher
44
@MikeDunlavey: un buen ejemplo es que construir las pirámides es O (n) mientras que ordenar tus fotos es O (n ln n), ¡pero es más rápido!
Martin Beckett
2
Hay algoritmos O (n log n) garantizados, como heapsort y mergesort, por lo que en términos asintóticos en el peor de los casos, Quicksort ni siquiera es tan rápido como el mejor. Pero en el rendimiento del mundo real, algunas variantes de clasificación rápida funcionan extremadamente bien. Sin embargo, decir "el coeficiente es más pequeño" es como decir "es más rápido porque es más rápido". ¿Por qué los factores constantes son tan pequeños? Una razón clave es que quicksort es muy bueno en términos de localidad: hace un muy buen uso de los cachés. Mergesort también tiene una buena localidad, pero es muy difícil hacerlo en el lugar.
Steve314
16

Quicksort no supera a todos los demás algoritmos de ordenación. Por ejemplo, la ordenación del montón de abajo hacia arriba ( Wegener 2002 ) supera a la clasificación rápida para cantidades razonables de datos y también es un algoritmo en el lugar. También es fácil de implementar (al menos, no más difícil que alguna variante optimizada de clasificación rápida).

Simplemente no es tan conocido y no lo encuentras en muchos libros de texto, eso puede explicar por qué no es tan popular como Quicksort.

Doc Brown
fuente
+1: He ejecutado algunas pruebas y, de hecho, combinar la clasificación fue definitivamente mejor que la clasificación rápida para matrices grandes (> 100000 elementos). La ordenación del montón fue ligeramente peor que la ordenación por fusión (pero la ordenación por fusión necesita más memoria). Creo que lo que la gente llama ordenación rápida es a menudo una variación llamada ordenación introductoria: ordenación rápida que recurre a la ordenación en montón cuando la profundidad de recursión supera un cierto límite.
Giorgio
@Giorgio: quicksort se puede modificar de alguna manera para mejorarlo, vea por ejemplo aquí: algs4.cs.princeton.edu/23quicksort ¿ Intentó esas mejoras?
Doc Brown
Interesante, ¿puedes encontrar una referencia a un libro \ sitio para leer más sobre él? (preferiblemente un libro)
Ramzi Kahil
@ Martin: ¿te refieres al montón de abajo hacia arriba? Bueno, di una referencia arriba. Si desea un recurso gratuito, la wikipedia alemana tiene un artículo al respecto ( de.wikipedia.org/wiki/BottomUp-Heapsort ). Incluso si no hablas alemán, supongo que aún puedes leer el ejemplo C99.
Doc Brown
7

No debe centrarse solo en el peor de los casos y solo en la complejidad del tiempo. Se trata más del promedio que de lo peor, y se trata del tiempo y el espacio.

Ordenación rápida:

  • tiene una complejidad de tiempo promedio de Θ ( n log n );
  • se puede implementar con la complejidad espacial de Θ (log n );

También tenga en cuenta que la notación O grande no tiene en cuenta ninguna constante, pero en la práctica sí hace la diferencia si el algoritmo es varias veces más rápido. Θ ( n log n ) significa que ese algoritmo se ejecuta en K  n  log ( n ), donde K es constante. Quicksort es el algoritmo de clasificación de comparación con la K más baja .

vartec
fuente
1
@Gilles: tiene una K baja, porque es un algoritmo simple.
Vartec
55
WTF? Esto no tiene ningún sentido. La simplicidad de un algoritmo no tiene relación con su velocidad de ejecución. La selección de selección es más simple que la clasificación rápida, eso no lo hace más rápido.
Gilles 'SO- deja de ser malvado'
1
@Gilles: el orden de selección es O (n ^ 2) para cualquier caso (peor, promedio y mejor). Por lo tanto, no importa cuán simple sea. Quicksort es O (n log n) para el caso promedio, y entre todos los algos con O (n log n) promedio es el más simple.
vartec
1
@Gilles: en igualdad de condiciones, la simplicidad ayuda al rendimiento. Digamos que está comparando dos algoritmos que cada uno toma (K n log n) iteraciones de sus respectivos bucles internos: el algoritmo que necesita hacer menos cosas por bucle tiene una ventaja de rendimiento.
tormenta de
1
@comingstorm: Dicho así, su declaración es una tautología, pero no se relaciona con la "simplicidad". Hay, por ejemplo, variantes más complicadas de Quicksort (distinciones de casos) que resultan en un tiempo de ejecución más pequeño (tanto en teoría como en práctica).
Raphael
5

Quicksort suele ser una buena opción, ya que es razonablemente rápido y razonablemente rápido y fácil de implementar.

Si te tomas en serio la clasificación de grandes cantidades de datos muy rápidamente, entonces probablemente estés mejor con alguna variación en MergeSort. Esto puede hacerse para aprovechar el almacenamiento externo, puede hacer uso de múltiples hilos o incluso procesos, pero no son triviales para el código.

James Anderson
fuente
1

El rendimiento real de los algoritmos depende de la plataforma, así como del lenguaje, el compilador, la atención del programador a los detalles de implementación, el esfuerzo de optimización específico, etc. Por lo tanto, la "ventaja de factor constante" de quicksort no está muy bien definida: es un juicio subjetivo basado en las herramientas disponibles actualmente y una estimación aproximada del "esfuerzo de implementación equivalente" por parte de quien realmente realiza el estudio comparativo de rendimiento. .

Dicho esto, creo que quicksort funciona bien (para entrada aleatoria) porque es simple y porque su estructura recursiva es relativamente amigable con la caché. Por otro lado, debido a que su peor caso es fácil de desencadenar, cualquier uso práctico de una clasificación rápida tendrá que ser más complejo de lo que su descripción del libro de texto indicaría: por lo tanto, versiones modificadas como introsort.

Con el tiempo, a medida que cambia la plataforma dominante, diferentes algoritmos pueden ganar o perder su ventaja relativa (mal definida). La sabiduría convencional sobre el rendimiento relativo puede retrasarse con respecto a este cambio, por lo que si no está seguro de qué algoritmo es mejor para su aplicación, debe implementar ambos y probarlos.

tormenta
fuente
Supongo que la "constante más pequeña" con la que otros lo relacionan es la del análisis formal, es decir, el número de comparaciones o intercambios. Esto está muy bien definido, pero no está claro cómo se traduce en tiempo de ejecución. Un colega actualmente investiga un poco sobre eso, en realidad.
Rafael
Mi impresión fue que se trataba de un rendimiento generalizado, pero tampoco contaría con eso. Sin embargo, tiene razón: si su comparación es particularmente costosa, puede buscar el número de comparaciones esperadas ...
tormenta que viene
1
Por la razón que declara, hablar sobre el rendimiento general (en cuanto al tiempo) no tiene sentido en el caso general, ya que se incluyen demasiados detalles. La razón para contar solo las operaciones seleccionadas no es que sean costosas, sino que ocurren "con mayor frecuencia" "en el sentido de la notación de Landau (Big-Oh), por lo que contarlos te da tus asintóticos toscos. Tan pronto como considere las constantes y / o el tiempo de ejecución, esta estrategia es mucho menos interesante.
Rafael
Una buena implementación de QuickSort compilará de tal manera que sus valores dinámicos permanezcan en un registro de CPU durante el tiempo que sean necesarios. Esto a menudo es suficiente para superar una clasificación teóricamente más rápida con tiempos Big-O comparables.
Dan Lyons
Los diferentes algoritmos de clasificación tienen características diferentes con respecto al número de comparaciones y al número de intercambios que realizan. Y @DanLyons nota que un tipo típico en una biblioteca realiza sus comparaciones a través de funciones proporcionadas por el usuario, y mantener valores en registros en muchas llamadas de funciones es bastante complicado.
Puntiagudo