Donc, fondamentalement, vous voulez savoir s'il existe un algorithme de tri qui ne se dégraderait pas de son cas moyen si on lui donnait une fonction de comparaison similaire à:
int Compare(object a, object b) { return Random.Next(-1,1); }
... où Random.Next () est une méthode qui produira un entier généré de façon aléatoire entre une limite inférieure et supérieure incluse incluse.
La réponse est en fait que la plupart des algorithmes de tri de base fonctionneront selon leur cas moyen, car ils obéissent à au moins l'une des deux conditions suivantes:
- Une comparaison entre deux éléments uniques n'est jamais effectuée deux fois dans le tri, et / ou
- Dans chaque itération du tri, la position correcte d'au moins un élément est déterminée et de sorte que cet élément n'est plus jamais comparé.
Par exemple, SelectionSort parcourt la sous-liste des éléments non triés, trouve l'élément "le moins" et / ou "le plus grand" (en comparant chacun au plus grand jusqu'à présent), le place dans sa position correcte et répète. Par conséquent, même avec un comparateur non déterministe, à la fin de chaque itération, l'algorithme aura trouvé une valeur qu'il pense être la plus petite ou la plus grande, l'échange avec l'élément dans la position qu'il essaie de déterminer, et ne considère jamais cet élément à nouveau, donc il obéit à la condition 2. Cependant, un A et un B peuvent être comparés plusieurs fois au cours de ce processus (comme l'exemple le plus extrême, considérez plusieurs passes de SelectionSort sur un tableau qui est trié dans l'ordre inverse) donc il viole la condition 1 .
MergeSort obéit à la condition 1 mais pas à la condition 2; lorsque les sous-tableaux sont fusionnés, les éléments du même sous-tableau (à gauche ou à droite) ne sont pas comparés les uns aux autres car il a déjà été déterminé que les éléments de ce côté du tableau sont en ordre entre eux; l'algorithme compare uniquement l'élément le moins non fusionné de chaque sous-tableau à l'autre pour déterminer celui qui est le moins important et devrait aller ensuite dans la liste fusionnée. Cela signifie que deux objets uniques A et B seront comparés l'un à l'autre au maximum une fois, mais l'index "final" d'un élément donné dans la collection complète n'est pas connu tant que l'algorithme n'est pas terminé.
InsertionSort n'obéit qu'à la condition 1 également, même si sa stratégie globale et sa complexité ressemblent davantage à SelectionSort. Chaque élément non trié est comparé aux éléments triés, le plus grand en premier, jusqu'à ce qu'il en trouve un de moins que l'élément examiné. l'élément est inséré à ce point, puis l'élément suivant est pris en compte. Le résultat est que l'ordre relatif de tout A et B est déterminé par une comparaison, et que d'autres comparaisons entre A et B ne sont jamais effectuées, mais la position finale d'un élément ne peut être connue que lorsque tous les éléments sont pris en compte.
QuickSort obéit aux deuxConditions. A chaque niveau, un pivot est choisi et agencé de telle sorte que le côté "gauche" contient des éléments inférieurs au pivot et le côté "droit" contient des éléments supérieurs au pivot. Le résultat de ce niveau est QuickSort (gauche) + pivot + QuickSort (droite) ce qui signifie essentiellement que la position de l'élément pivot est connue (un index supérieur à la longueur du côté gauche), le pivot n'est jamais comparé à aucun autre élément après qu'il a été choisi comme pivot (il peut avoir été comparé aux éléments de pivot précédents, mais ces éléments sont également connus et ne sont inclus dans aucun sous-réseau), ET les A et B qui se retrouvent sur les côtés opposés du pivot ne sont jamais par rapport. Dans la plupart des implémentations de QuickSort pur, le scénario de base est un élément, auquel cas son index actuel est son index final et aucune autre comparaison n'est effectuée.
Le seul type comparatif auquel je peux penser qui n'obéirait à aucune de ces conditions est un BubbleSort non optimisé. Si le tri n'accepte pas que les X éléments les plus importants soient à leur place après l'exécution de X passes et / ou utilise une passe de "double vérification" pour vérifier que la liste est triée, le tri ne sera considéré comme "terminé" que lorsque le comparateur aléatoire est retourné -1 ou 0 pour tous les deux éléments adjacents de la liste pendant une passe et donc aucun swap ont été réalisées (un événement qui, si vraiment aléatoire, qui se produirait avec une probabilité ; pour une relativement petite liste de 25 éléments, c'est une chance sur 2000, alors que pour 100 éléments la probabilité est de 3,7 * 10 -18(2/3)N−1). Au fur et à mesure que la valeur absolue maximale du résultat du comparateur augmente, la probabilité pour une comparaison de retourner un résultat négatif ou nul diminue vers 0,5, ce qui rend la chance de terminer l'algorithme beaucoup moins probable (la chance de 99 pièces fait basculer toutes les têtes d'atterrissage , qui est essentiellement ce que cela se résume à, est de 1 sur 1,2 * 10 30 )
MODIFIER LONGTEMPS PLUS TARD: Il y a quelques "sortes" conçues spécifiquement comme exemples de ce qu'il ne faut pas faire qui incorporent un comparateur aléatoire; peut-être le plus célèbre est BogoSort. "Étant donné une liste, si la liste n'est pas en ordre, mélangez la liste et vérifiez à nouveau". Théoriquement, il finira par atteindre la bonne permutation des valeurs, tout comme le "BubbleSort non optimisé" ci-dessus, mais le cas moyen est le temps factoriel (N! / 2), et en raison du problème d'anniversaire (après suffisamment de permutations aléatoires, vous devenir plus susceptibles de rencontrer des permutations en double que des permutations uniques), il existe une possibilité non nulle que l'algorithme ne se termine jamais officiellement, l'algorithme est illimité dans le temps.