web-dev-qa-db-fra.com

Algorithme pour fusionner deux tableaux triés avec un nombre minimum de comparaisons

Donné sont deux tableaux triés a , b de type [~ # ~] t [~ # ~] avec la taille n et m . Je recherche un algorithme qui fusionne les deux tableaux dans un nouveau tableau (de taille maximale n + m).

Si vous avez une opération de comparaison bon marché, c'est assez simple. Prenez simplement dans le tableau avec le premier élément le plus bas jusqu'à ce qu'un ou les deux tableaux soient complètement traversés, puis ajoutez les éléments restants. Quelque chose comme ça https://stackoverflow.com/questions/5958169/how-to-merge-two-sorted-arrays-into-a-sorted-array

Cependant, la situation change lorsque la comparaison de deux éléments est beaucoup plus coûteuse que la copie d'un élément du tableau source vers le tableau cible. Par exemple, vous pouvez avoir un tableau de grands nombres entiers de précision arbitraire, ou chaînes, où une comparaison peut être assez coûteuse. Supposons simplement que la création de tableaux et la copie d'éléments sont gratuits, et la seule chose qui coûte est de comparer les éléments.

Dans ce cas, vous souhaitez fusionner les deux tableaux avec un nombre minimum de comparaisons d'éléments. Voici quelques exemples où vous devriez pouvoir faire beaucoup mieux que l'algorithme de fusion simple:

a = [1,2,3,4, ... 1000]
b = [1001,1002,1003,1004, ... 2000]

Ou

a = [1,2,3,4, ... 1000]
b = [0,100,200, ... 1000]

Il y a des cas où l'algorithme de fusion simple sera optimal, comme

a = [1,3,5,7,9,....,999]
b = [2,4,6,8,10,....,1000]

Donc, l'algorithme devrait idéalement se dégrader avec élégance et effectuer un maximum de n + m-1 comparaisons au cas où les tableaux seraient entrelacés, ou du moins ne seraient pas significativement plus mauvais.

Une chose qui devrait très bien fonctionner pour les listes avec une grande différence de taille serait d'utiliser la recherche binaire pour insérer les éléments du plus petit tableau dans le plus grand tableau. Mais cela ne se dégradera pas gracieusement dans le cas où les deux listes sont de la même taille et entrelacées.

La seule chose disponible pour les éléments est une fonction de commande (totale), donc tout schéma qui rend les comparaisons moins chères n'est pas possible.

Des idées?

J'ai trouvé ce bit en Scala . Je pense qu'il est optimal en ce qui concerne le nombre de comparaisons, mais il est au-delà de ma capacité de le prouver. Au moins, c'est beaucoup plus simple que ce que j'ai trouvé dans la littérature.

Et depuis la publication d'origine, j'ai écrit un article de blog sur la façon dont cela fonctionne.

24
Rüdiger Klaehn

L'algorithme de tri par fusion normal - étape de fusion avec normalement des comparaisons n + m -1, où une liste est de taille n et l'autre liste est de taille m. L'utilisation de cet algorithme est l'approche la plus simple pour combiner deux listes triées.

Si les comparaisons sont trop chères, vous pouvez faire deux choses: soit vous réduisez le nombre de comparaisons, soit vous réduisez le coût des comparaisons.

Concentrons-nous sur la minimisation du coût de comparaison. Vous et vous seul pouvez décider si les données que vous comparez peuvent être quantifiées ou non. Si vous pouvez les quantifier, c'est une forme d'implémentation d'une méthode de hachage, qui maintient l'ordre. Par exemple. si vos données sont comparées par nom, puis le premier tname, ... vous pouvez prendre le premier aux caractères du nom "Klaehn, Ruediger" et réduire/quantifier votre élément de données à "Kl.Ru", si vous le comparez à "Packer, The" vous conservez la commande "Pa.Th" - vous pouvez maintenant appliquer un algorithme de comparaison moins cher, en comparant les valeurs réduites. Mais si vous trouvez un autre "Kl.Ru", vous avez maintenant une valeur proche, et vous pouvez maintenant passer à une approche plus coûteuse en comparant ces éléments.

Si vous pouvez extraire cette valeur quantifiée de vos données, plus rapidement que de la comparer, c'est la première chose que vous faites, vous comparez d'abord la valeur quantifiée ou hachée. N'oubliez pas que cette valeur ne doit être calculée qu'une seule fois, vous pouvez donc la calculer lors de la création de l'élément de données.

J'ai également mentionné une autre façon, de minimiser vos comparaisons.

J'ai jeté un œil au livre classique TAOCP-Volume 3-Sorting and Searching, (pp.197-207, section 5.3.2) qui contient 10 pages complètes sur ce sujet. J'ai trouvé deux références à des algorithmes qui sont plus rapides que les comparaisons n + m-1.

Il y a d'abord l'algorithme de fusion Hwang-Lin et le second une amélioration par Glenn K Manacher - tous deux sont cités par TAOCP ainsi qu'un algorithme par Christen, qui se rapproche de la limite inférieure des comparaisons nécessaires, à des conditions spéciales sur la longueur n et m des listes.

L'algorithme de Manacher a été présenté dans Journal of the ACM Vol. 26 Numéro 3 aux pages 434-440: "Améliorations significatives de l'algorithme de fusion" Hwan-Lin "". la liste avec m éléments et la liste avec n éléments peuvent être de longueur différente, mais elles doivent également être différenciées par le nombre d'éléments qu'elles contiennent m <= n

L'algorithme Hwang-Lin décompose les listes à fusionner, en dehors de listes plus petites et trie les listes en comparant le premier élément de chaque sous-liste, et de décider si certains éléments de la sous-liste doivent être comparés ou non. Si la première liste est plus petite que la deuxième liste, alors il y a de fortes chances que des éléments consécutifs de la liste plus longue puissent être transférés dans la liste résultante sans comparaison. Si le premier élément du petit ist est supérieur au premier élément de la grande liste divisée, tous les éléments devant la sous-liste peuvent être copiés sans comparaison.

Analyse de cas moyenne de l'alorithme de fusion de Hwang et Lin (Vega, Frieze, Santha) dans la section 2, vous pouvez trouver un pseudocode de l'algorithme HL. Ce qui est bien mieux que ma description. Et vous pouvez voir pourquoi il y a moins de comparaisons - l'algorithme utilise une recherche binaire, pour trouver l'index, où insérer l'élément de la liste plus courte.

Si les listes ne sont pas entrelacées comme dans votre dernier exemple, vous devriez avoir une liste plus petite et une plus grande dans la plupart des cas. C'est alors que l'algorithme HL commence à mieux fonctionner.

31
thepacker

Supposons que les deux tableaux ont N et M éléments, N ≥ M, et tous les éléments sont différents.

Si le tableau trié contient un élément x de N suivi d'un élément y de M ou vice versa, alors x et y doivent avoir été comparés, sinon nous ne saurions pas dans quel ordre ils appartiennent. (Il ne peut pas y avoir une chaîne d'autres éléments disons a, b, c où nous savons que x <a <b <c <y, par exemple, parce qu'il n'y a pas d'éléments entre x et y. Donc x et y doivent avoir été comparés directement.

Si N> M, il est possible d'avoir un tableau où chaque élément de M est à la fois précédé et suivi d'un élément de N, ce qui signifie qu'au moins 2M comparaisons sont nécessaires - même si vous utilisez un algorithme de tri non déterministe qui peut faire une estimation parfaite des chiffres à comparer. (Ce que cela signifie: Supposons que vous ayez N grand, M = 1. La recherche binaire prend O (log2 N) étapes; un algorithme non déterministe devinerait entre quels deux éléments appartient l'un des éléments du second tableau et ferait deux comparaisons avec confirmer la supposition).

1
gnasher729