Je sais que la question n'est pas trop précise.
Tout ce que je veux, c'est que quelqu'un me dise comment convertir un tri de fusion normal en un tri de fusion sur place (ou un tri de fusion avec un espace supplémentaire constant).
Tout ce que je peux trouver (sur le net), c'est des pages disant "c'est trop complexe" ou "hors de portée de ce texte".
Les seules façons connues de fusionner sur place (sans espace supplémentaire) sont trop complexes pour être réduites à un programme pratique. (extrait d'ici )
Même s'il est trop complexe, quel est le concept de base sur la façon de rendre le tri de fusion en place?
Réponses:
Knuth a laissé cela comme un exercice (Vol 3, 5.2.5). Il existe des sortes de fusion sur place. Ils doivent être mis en œuvre avec soin.
Tout d'abord, la fusion naïve sur place telle que décrite ici n'est pas la bonne solution. Il rétrograde les performances à O (N 2 ) .
L'idée est de trier une partie du tableau tout en utilisant le reste comme zone de travail pour la fusion.
Par exemple, comme la fonction de fusion suivante.
Il prend le tableau
xs
, les deux sous-tableaux triés sont représentés sous forme de plages[i, m)
et[j, n)
respectivement. La zone de travail commence à partir dew
. Comparer avec l'algorithme de fusion standard donné dans la plupart des manuels, celui-ci échange le contenu entre le sous-tableau trié et la zone de travail. Par conséquent, la zone de travail précédente contient les éléments triés fusionnés, tandis que les éléments précédents stockés dans la zone de travail sont déplacés vers les deux sous-tableaux.Cependant, deux contraintes doivent être satisfaites:
Avec cet algorithme de fusion défini, il est facile d'imaginer une solution, qui peut trier la moitié du tableau; La question suivante est de savoir comment traiter le reste de la partie non triée stockée dans la zone de travail, comme indiqué ci-dessous:
Une idée intuitive est de trier récursivement une autre moitié de la zone de travail, il n'y a donc que 1/4 des éléments qui n'ont pas encore été triés.
Le point clé à ce stade est que nous devons fusionner les 1/4 éléments triés B avec les 1/2 éléments triés A tôt ou tard.
La zone de travail reste-t-elle, qui ne contient que 1/4 des éléments, assez grande pour fusionner A et B? Ce n'est malheureusement pas le cas.
Cependant, la deuxième contrainte mentionnée ci-dessus nous donne un indice, que nous pouvons l'exploiter en arrangeant la zone de travail pour qu'elle se chevauche avec l'un ou l'autre sous-tableau si nous pouvons garantir la séquence de fusion que les éléments non fusionnés ne seront pas écrasés.
En fait, au lieu de trier la seconde moitié de la zone de travail, nous pouvons trier la première moitié et placer la zone de travail entre les deux tableaux triés comme ceci:
Cette configuration organise efficacement le chevauchement de la zone de travail avec le sous-réseau A. Cette idée est proposée dans [Jyrki Katajainen, Tomi Pasanen, Jukka Teuhola. `` Mergesort pratique sur place ''. Nordic Journal of Computing, 1996].
Donc, la seule chose qui reste est de répéter l'étape ci-dessus, ce qui réduit la zone de travail de 1/2, 1/4, 1/8,… Lorsque la zone de travail devient suffisamment petite (par exemple, il ne reste que deux éléments), nous pouvons passer à un tri d'insertion trivial pour terminer cet algorithme.
Voici l'implémentation en ANSI C basée sur cet article.
Où wmerge est défini précédemment.
Le code source complet se trouve ici et l'explication détaillée se trouve ici
Soit dit en passant, cette version n'est pas le tri de fusion le plus rapide car elle nécessite plus d'opérations de swap. Selon mon test, c'est plus rapide que la version standard, qui alloue des espaces supplémentaires à chaque récursivité. Mais c'est plus lent que la version optimisée, qui double à l'avance le tableau d'origine et l'utilise pour une fusion ultérieure.
la source
Knuth left this as an exercise (Vol 3, 5.2.5).
fait référence à ex. 13. [40] Implémentez la méthode de tri interne suggérée [à la fin de cette section], produisant qui trie les données aléatoires en O (N) unités de temps avec seulement O (sqrt (N)) emplacements de mémoire supplémentaires. ? ( 40 indiquant un problème assez difficile ou long qui peut peut-être convenir comme projet à terme dans des situations de classe. )Incluant son "grand résultat", cet article décrit quelques variantes du tri par fusion sur place (PDF):
http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.22.5514&rep=rep1&type=pdf
Tri sur place avec moins de mouvements
Jyrki Katajainen, Tomi A. Pasanen
Je pense que cela est également pertinent. J'en ai une copie imprimée qui m'a été remise par un collègue, mais je ne l'ai pas lue. Il semble couvrir la théorie de base, mais je ne connais pas suffisamment le sujet pour juger de manière exhaustive:
http://comjnl.oxfordjournals.org/cgi/content/abstract/38/8/681
Fusion stable optimale
Antonios Symvonis
la source
Ce n'est vraiment pas facile ou efficace, et je vous suggère de ne pas le faire à moins que vous ne deviez vraiment le faire (et vous n'avez probablement pas à le faire à moins que ce ne soit des devoirs puisque les applications de la fusion sur place sont principalement théoriques). Vous ne pouvez pas utiliser quicksort à la place? Quicksort sera de toute façon plus rapide avec quelques optimisations plus simples et sa mémoire supplémentaire est O (log N) .
Quoi qu'il en soit, si vous devez le faire, vous devez. Voici ce que j'ai trouvé: un et deux . Je ne connais pas le tri par fusion interne, mais il semble que l'idée de base consiste à utiliser des rotations pour faciliter la fusion de deux tableaux sans utiliser de mémoire supplémentaire.
Notez que cela est même plus lent que le tri de fusion classique qui n'est pas en place.
la source
L'étape critique consiste à mettre en place la fusion elle-même. Ce n'est pas aussi difficile que le disent ces sources, mais vous perdez quelque chose lorsque vous essayez.
En regardant une étape de la fusion:
Nous savons que la Sorted séquence est inférieure à tout le reste, que x est inférieur à tout le reste en A , et que y est inférieur à tout le reste en B . Dans le cas où x est inférieur ou égal à y , il vous suffit de déplacer votre pointeur vers le début de A sur un. Dans le cas où y est inférieur à x , vous devez mélanger y après l'ensemble de A pour trier . C'est cette dernière étape qui fait que cela coûte cher (sauf dans les cas dégénérés).
Il est généralement moins cher (en particulier lorsque les tableaux ne contiennent en fait que des mots uniques par élément, par exemple, un pointeur vers une chaîne ou une structure) pour échanger de l'espace pour le temps et avoir un tableau temporaire séparé que vous triez dans les deux sens.
la source
Juste pour référence, voici une belle implémentation d'un tri de fusion in situ stable . Compliqué, mais pas trop mal.
J'ai fini par implémenter à la fois un tri de fusion sur place stable et un tri rapide sur place stable en Java. Veuillez noter que la complexité est O (n (log n) ^ 2)
la source
Un exemple de fusion sans tampon en C.
Un exemple de fusion adaptative (optimisé).
Ajoute du code de support et des modifications pour accélérer la fusion lorsqu'un tampon auxiliaire de n'importe quelle taille est disponible (fonctionne toujours sans mémoire supplémentaire). Utilise la fusion avant et arrière, la rotation en anneau, la fusion et le tri de petites séquences et la fusion itérative.
la source
Cette réponse contient un exemple de code qui implémente l'algorithme décrit dans l'article Practical In-Place Merging par Bing-Chao Huang et Michael A. Langston. Je dois admettre que je ne comprends pas les détails, mais la complexité donnée de l'étape de fusion est O (n).
D'un point de vue pratique, il est prouvé que les implémentations pures sur place ne fonctionnent pas mieux dans des scénarios du monde réel. Par exemple, la norme C ++ définit std :: inplace_merge , qui est comme son nom l'indique une opération de fusion sur place.
En supposant que les bibliothèques C ++ sont généralement très bien optimisées, il est intéressant de voir comment elles sont implémentées:
1) libstdc ++ (partie de la base de code GCC): std :: inplace_merge
L'implémentation délègue à __inplace_merge , ce qui évite le problème en essayant d'allouer un tampon temporaire:
Sinon, il revient à une implémentation ( __merge_without_buffer ), qui ne nécessite pas de mémoire supplémentaire, mais ne s'exécute plus en temps O (n).
2) libc ++ (partie de la base de code Clang): std :: inplace_merge
Ressemble similaire. Il délègue à une fonction , qui essaie également d' allouer un tampon . Selon qu'il aura suffisamment d'éléments, il choisira l'implémentation. La fonction de secours à mémoire constante est appelée __buffered_inplace_merge .
Peut-être que même la solution de rechange est toujours O (n), mais le fait est qu'ils n'utilisent pas l'implémentation si de la mémoire temporaire est disponible.
Notez que la norme C ++ donne explicitement aux implémentations la liberté de choisir cette approche en réduisant la complexité requise de O (n) à O (N log N):
Bien sûr, cela ne peut pas être considéré comme une preuve que l'espace constant en place fusionne en temps O (n) ne doit jamais être utilisé. D'un autre côté, si c'était plus rapide, les bibliothèques C ++ optimisées passeraient probablement à ce type d'implémentation.
la source
Voici ma version C:
la source
Il existe une implémentation relativement simple du tri par fusion sur place utilisant la technique originale de Kronrod mais avec une implémentation plus simple. Un exemple illustré qui illustre cette technique peut être trouvé ici: http://www.logiccoder.com/TheSortProblem/BestMergeInfo.htm .
Il existe également des liens vers une analyse théorique plus détaillée du même auteur associée à ce lien.
la source
Je viens d'essayer l'algorithme de fusion sur place pour le tri par fusion dans JAVA en utilisant l'algorithme de tri par insertion, en suivant les étapes suivantes.
1) Deux tableaux triés sont disponibles.
2) Comparez les premières valeurs de chaque tableau; et placez la plus petite valeur dans le premier tableau.
3) Placez la plus grande valeur dans le deuxième tableau en utilisant le tri par insertion (traversée de gauche à droite).
4) Comparez à nouveau la deuxième valeur du premier tableau et la première valeur du deuxième tableau, et faites de même. Mais lorsque l'échange se produit, il est possible de ne pas comparer les autres éléments, mais simplement l'échange requis.
J'ai fait une optimisation ici; pour conserver des comparaisons moindres dans le tri par insertion.
Le seul inconvénient que j'ai trouvé avec cette solution est qu'elle nécessite un échange plus important des éléments du tableau dans le deuxième tableau.
par exemple)
First___Array: 3, 7, 8, 9
Deuxième tableau: 1, 2, 4, 5
Ensuite, 7, 8, 9 oblige le second tableau à échanger (déplacer à gauche d'un) tous ses éléments un à chaque fois pour se placer dans le dernier.
Donc, l'hypothèse ici est que l'échange d'articles est négligeable par rapport à la comparaison de deux articles.
https://github.com/skanagavelu/algorithams/blob/master/src/sorting/MergeSort.java
la source