Sé que la pregunta no es demasiado específica.
Todo lo que quiero es que alguien me diga cómo convertir una ordenación de fusión normal en una ordenación de fusión en el lugar (o una ordenación de fusión con una sobrecarga de espacio adicional constante).
Todo lo que puedo encontrar (en la red) son páginas que dicen "es demasiado complejo" o "fuera del alcance de este texto".
Las únicas formas conocidas de fusionarse en el lugar (sin espacio adicional) son demasiado complejas para reducirlas a un programa práctico. (tomado de aquí )
Incluso si es demasiado complejo, ¿cuál es el concepto básico de cómo ordenar la fusión en el lugar?
Respuestas:
Knuth dejó esto como un ejercicio (Vol. 3, 5.2.5). Existen tipos de fusión en el lugar. Deben implementarse con cuidado.
Primero, la fusión ingenua en el lugar como se describe aquí no es la solución correcta. Reduce el rendimiento a O (N 2 ) .
La idea es ordenar parte de la matriz mientras se usa el resto como área de trabajo para la fusión.
Por ejemplo, como la siguiente función de fusión.
Toma la matriz
xs
, las dos sub-matrices ordenadas se representan como rangos[i, m)
y[j, n)
respectivamente. El área de trabajo comienza desdew
. Compare con el algoritmo de fusión estándar dado en la mayoría de los libros de texto, este intercambia el contenido entre el subarreglo ordenado y el área de trabajo. Como resultado, el área de trabajo anterior contiene los elementos ordenados combinados, mientras que los elementos anteriores almacenados en el área de trabajo se mueven a las dos sub-matrices.Sin embargo, hay dos restricciones que deben cumplirse:
Con este algoritmo de fusión definido, es fácil imaginar una solución, que puede clasificar la mitad de la matriz; La siguiente pregunta es, cómo lidiar con el resto de la parte sin clasificar almacenada en el área de trabajo como se muestra a continuación:
Una idea intuitiva es ordenar recursivamente otra mitad del área de trabajo, por lo tanto, solo hay 1/4 elementos que aún no se han ordenado.
El punto clave en esta etapa es que debemos fusionar los 1/4 elementos B ordenados con los 1/2 elementos A ordenados, tarde o temprano.
¿Queda el área de trabajo, que solo contiene 1/4 elementos, lo suficientemente grande como para fusionar A y B? Lamentablemente, no lo es.
Sin embargo, la segunda restricción mencionada anteriormente nos da una pista, de que podemos explotarla organizando el área de trabajo para que se superponga con cualquiera de los subconjuntos si podemos asegurar la secuencia de fusión de que los elementos no fusionados no se sobrescribirán.
En realidad, en lugar de ordenar la segunda mitad del área de trabajo, podemos ordenar la primera mitad y colocar el área de trabajo entre las dos matrices ordenadas de esta manera:
Esta configuración organiza efectivamente la superposición del área de trabajo con la sub-matriz A. Esta idea se propone en [Jyrki Katajainen, Tomi Pasanen, Jukka Teuhola. `` Práctico mergesort en el lugar ''. Revista nórdica de informática, 1996].
Entonces, lo único que queda es repetir el paso anterior, que reduce el área de trabajo de 1/2, 1/4, 1/8, ... Cuando el área de trabajo se vuelve lo suficientemente pequeña (por ejemplo, solo quedan dos elementos), podemos cambie a un tipo de inserción trivial para finalizar este algoritmo.
Aquí está la implementación en ANSI C basada en este documento.
Donde wmerge se define previamente.
El código fuente completo se puede encontrar aquí y la explicación detallada se puede encontrar aquí
Por cierto, esta versión no es el tipo de fusión más rápido porque necesita más operaciones de intercambio. Según mi prueba, es más rápido que la versión estándar, que asigna espacios adicionales en cada recursión. Pero es más lento que la versión optimizada, que duplica la matriz original por adelantado y la usa para una mayor fusión.
fuente
Knuth left this as an exercise (Vol 3, 5.2.5).
se refiere a ex. 13. [40] Implementar el método interno de clasificación sugerido [en el cierre de esta sección], produciendo que clasifica datos aleatorios en O (N) unidades de mith tiempo sólo O (sqrt (n)) posiciones de memoria adicionales. ? ( 40 indica un problema bastante difícil o largo que tal vez sea adecuado como un proyecto de término en situaciones de clase. )Incluyendo su "gran resultado", este documento describe un par de variantes del tipo de fusión in situ (PDF):
http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.22.5514&rep=rep1&type=pdf
Clasificación in situ con menos movimientos
Jyrki Katajainen, Tomi A. Pasanen
Creo que esto también es relevante. Tengo una copia impresa por ahí, entregada por un colega, pero no la he leído. Parece cubrir la teoría básica, pero no estoy lo suficientemente familiarizado con el tema para juzgar cuán exhaustivamente:
http://comjnl.oxfordjournals.org/cgi/content/abstract/38/8/681
Óptima fusión estable
Antonios Symvonis
fuente
Realmente no es fácil ni eficiente, y sugiero que no lo haga a menos que realmente tenga que hacerlo (y probablemente no tenga que hacerlo a menos que esto sea tarea ya que las aplicaciones de la fusión in situ son en su mayoría teóricas). ¿No puedes usar quicksort en su lugar? Quicksort será más rápido de todos modos con algunas optimizaciones más simples y su memoria adicional es O (log N) .
De todos modos, si debes hacerlo, entonces debes hacerlo. Esto es lo que encontré: uno y dos . No estoy familiarizado con el tipo de fusión in situ, pero parece que la idea básica es usar rotaciones para facilitar la fusión de dos matrices sin usar memoria adicional.
Tenga en cuenta que esto es más lento incluso que el clásico tipo de fusión que no está en su lugar.
fuente
El paso crítico es conseguir la fusión. esté en su lugar. No es tan difícil como lo hacen esas fuentes, pero pierdes algo cuando lo intentas.
Mirando un paso de la fusión:
Sabemos que la ordenada secuencia es menos de todo lo demás, que x es menor que todo lo demás en A , y que y es menos de todo lo demás en B . En el caso de que x sea menor o igual que y , simplemente mueva su puntero al comienzo de A en uno. En el caso de que y sea menor que x , debe barajar y pasar todo A para ordenar . El último paso es lo que lo hace costoso (excepto en casos degenerados).
En general, es más barato (especialmente cuando las matrices solo contienen palabras individuales por elemento, por ejemplo, un puntero a una cadena o estructura) para intercambiar algo de espacio por tiempo y tener una matriz temporal separada entre las que puede ordenar de un lado a otro.
fuente
Solo como referencia, aquí hay una buena implementación de un tipo de fusión estable en el lugar . Complicado, pero no está mal.
Terminé implementando un tipo de fusión estable en el lugar y una clasificación rápida en el lugar estable en Java. Tenga en cuenta que la complejidad es O (n (log n) ^ 2)
fuente
Un ejemplo de mergesort sin buffer en C.
Un ejemplo de mergesort adaptativo (optimizado).
Agrega código de soporte y modificaciones para acelerar la fusión cuando hay disponible un búfer auxiliar de cualquier tamaño (aún funciona sin memoria adicional). Utiliza la fusión hacia adelante y hacia atrás, la rotación del anillo, la fusión y clasificación de secuencias pequeñas y la fusión iterativa.
fuente
Esta respuesta tiene un ejemplo de código , que implementa el algoritmo descrito en el documento Practical In-Place Merging por Bing-Chao Huang y Michael A. Langston. Tengo que admitir que no entiendo los detalles, pero la complejidad dada del paso de fusión es O (n).
Desde una perspectiva práctica, existe evidencia de que las implementaciones in situ puras no funcionan mejor en escenarios del mundo real. Por ejemplo, el estándar C ++ define std :: inplace_merge , que es como su nombre implica una operación de fusión in situ.
Suponiendo que las bibliotecas de C ++ suelen estar muy bien optimizadas, es interesante ver cómo se implementa:
1) libstdc ++ (parte de la base del código GCC): std :: inplace_merge
La implementación delega a __inplace_merge , que esquiva el problema al tratar de asignar un búfer temporal:
De lo contrario, recurre a una implementación ( __merge_without_buffer ), que no requiere memoria adicional, pero ya no se ejecuta en tiempo O (n).
2) libc ++ (parte de la base del código Clang): std :: inplace_merge
Se ve similar. Delega a una función , que también intenta asignar un búfer . Dependiendo de si tiene suficientes elementos, elegirá la implementación. La función de reserva de memoria constante se llama __buffered_inplace_merge .
Tal vez incluso el respaldo todavía sea tiempo O (n), pero el punto es que no usan la implementación si hay memoria temporal disponible.
Tenga en cuenta que el estándar C ++ brinda explícitamente a las implementaciones la libertad de elegir este enfoque al reducir la complejidad requerida de O (n) a O (N log N):
Por supuesto, esto no puede tomarse como una prueba de que el espacio constante en el lugar se fusiona en el tiempo O (n) nunca debe usarse. Por otro lado, si fuera más rápido, las bibliotecas optimizadas de C ++ probablemente cambiarían a ese tipo de implementación.
fuente
Esta es mi versión C:
fuente
Existe una implementación relativamente simple de ordenamiento por fusión en el lugar utilizando la técnica original de Kronrod pero con una implementación más simple. Un ejemplo ilustrativo que ilustra esta técnica se puede encontrar aquí: http://www.logiccoder.com/TheSortProblem/BestMergeInfo.htm .
También hay enlaces a análisis teóricos más detallados del mismo autor asociados con este enlace.
fuente
Acabo de probar el algoritmo de fusión para la ordenación por fusión en JAVA usando el algoritmo de ordenación por inserción, siguiendo los siguientes pasos.
1) Dos arreglos ordenados están disponibles.
2) Compare los primeros valores de cada matriz; y coloque el valor más pequeño en la primera matriz.
3) Coloque el valor más grande en la segunda matriz utilizando el orden de inserción (transversal de izquierda a derecha).
4) Luego, vuelva a comparar el segundo valor de la primera matriz y el primer valor de la segunda matriz, y haga lo mismo. Pero cuando ocurre el intercambio, hay alguna pista sobre la omisión de comparar los elementos adicionales, pero solo se requiere el intercambio.
He hecho alguna optimización aquí; para mantener comparaciones menores en el tipo de inserción.
El único inconveniente que encontré con estas soluciones es que necesita un mayor intercambio de elementos de la matriz en la segunda matriz.
p.ej)
Primero___Array: 3, 7, 8, 9
Segunda matriz: 1, 2, 4, 5
Entonces 7, 8, 9 hace que la segunda matriz intercambie (se mueva hacia la izquierda por uno) todos sus elementos por uno cada vez para colocarse en el último.
Entonces, la suposición aquí es el intercambio de elementos es insignificante en comparación con la comparación de dos elementos.
https://github.com/skanagavelu/algorithams/blob/master/src/sorting/MergeSort.java
fuente