web-dev-qa-db-fra.com

Pourquoi Collections.sort utilise le tri par fusion au lieu du tri rapide?

Nous savons que le tri rapide est l'algorithme de tri le plus rapide.

Le fichier collections.sort utilisait un algorithme de tri par fusion plutôt qu'un tri rapide. Mais Arrays.sort utilise un tri rapide.

Quelle est la raison pour laquelle Collections.sort utilise le tri par fusion au lieu du tri rapide?

90
MayurB

Très probablement de Josh Bloch § :

J'ai écrit ces méthodes, alors je suppose que je suis qualifié pour répondre. Il est vrai qu’il n’existe pas de meilleur algorithme de tri. QuickSort présente deux inconvénients majeurs par rapport à mergesort:

  1. Ce n'est pas stable (comme parsifal l'a noté).

  2. Cela ne garantit pas n log n performance; il peut dégrader la performance quadratique sur les entrées pathologiques.

La stabilité n'est pas un problème pour les types primitifs, car il n'y a pas de notion d'identité distincte de l'égalité (de valeur). Et la possibilité d'un comportement quadratique n'a pas été considérée comme un problème pratique pour Bentely et l'implémentation de McIlroy (ou ultérieurement pour Dual Pivot Quicksort ), c'est pourquoi ces variantes de QuickSort ont été utilisées pour les tris primitifs .

La stabilité est un gros problème lors du tri d'objets arbitraires. Par exemple, supposons que vous ayez des objets représentant des courriers électroniques et que vous les triiez d'abord par date, puis par expéditeur. Vous vous attendez à ce qu'ils soient triés par date dans chaque expéditeur, mais cela ne sera vrai que si le tri est stable. C'est pourquoi nous avons choisi de fournir un tri stable (tri par fusion) pour trier les références d'objet. (D'un point de vue technique, plusieurs tris stables séquentiels résultent en un ordre lexicographique des clés dans l'ordre inverse du tri: le tri final détermine la sous-clé la plus significative.)

L’avantage de Nice, c’est que Merge Sort garantit les performances n log n (time), quelle que soit l’entrée. Bien sûr, il y a un inconvénient: le tri rapide est un tri "en place": il ne requiert que le log n espace externe (pour conserver la pile d'appels). Fusionner, trier, en revanche, nécessite O(n) espace externe. La variante TimSort (introduite dans Java SE 6) nécessite beaucoup moins d'espace (O (k)) si le tableau en entrée est presque trié.

De plus, le suivant est pertinent:

L'algorithme utilisé par Java.util.Arrays.sort et (indirectement) par Java.util.Collections.sort pour trier les références d'objet est un "mergesort modifié (dans lequel la fusion est omise si l'élément le plus élevé de la sous-liste basse est inférieur à l'élément le plus bas de la haute sous-liste). " C'est un type stable assez rapide qui garantit les performances de O (n log n) et requiert O(n) espace supplémentaire. À son époque (il a été écrit en 1997 par Joshua Bloch), c'était un bon choix, mais aujourd'hui, nous pouvons faire beaucoup mieux.

Depuis 2003, le tri par liste de Python utilise un algorithme appelé timsort (d'après Tim Peters, qui l'a écrit). Il s'agit d'un mergesort stable, adaptatif et itératif qui nécessite beaucoup moins que n comparaisons log (n) lorsqu'il est exécuté sur des baies partiellement triées, tout en offrant des performances comparables à celles d'un fusil traditionnel traditionnel lorsqu'il est exécuté sur des baies aléatoires. Comme tous les fusionnements appropriés, timsort est stable et s'exécute dans le temps O (n log n) (dans le pire des cas). Dans le pire des cas, timsort nécessite un espace de stockage temporaire pour n/2 références d'objet; dans le meilleur des cas, il ne nécessite qu'une petite quantité d'espace constante. Cela contraste avec l'implémentation actuelle, qui nécessite toujours un espace supplémentaire pour n références d'objet, et bat n log n uniquement sur des listes presque triées.

Timsort est décrit en détail ici: http: //svn.python.org/projects/python/trunk/Objects/listsort.txt .

L'implémentation d'origine de Tim Peters est écrite en C. Joshua Bloch l'a porté de C à Java et a été testé, référencé et optimisé à fond le code obtenu. Le code résultant est un remplacement instantané de Java.util.Arrays.sort. Sur des données hautement ordonnées, ce code peut s'exécuter jusqu'à 25 fois plus rapidement que l'implémentation actuelle (sur la machine virtuelle du serveur HotSpot). Sur des données aléatoires, les vitesses des anciennes et des nouvelles implémentations sont comparables. Pour les listes très courtes, la nouvelle implémentation est nettement plus rapide que l'ancienne, même sur des données aléatoires (car elle évite une copie inutile des données).

Voir aussi Est-ce que Java 7 utilise Tim Sort pour les méthodes Arrays.Sort? .

Il n'y a pas un seul "meilleur" choix. Comme pour beaucoup d'autres choses, il s'agit de compromis.

175
NPE