Quel est l'algorithme de tri à espace constant le plus efficace?

19

Je recherche un algorithme de tri pour les tableaux int qui n'alloue aucun octet autre que la taille du tableau et est limité à deux instructions:

  1. SWAP: échange le prochain index avec celui en cours;

  2. MOVE: déplace le curseur sur un index +1 ou -1;

Autrement dit, vous ne pouvez pas échanger des index non voisins, ni échanger l'index 100, après avoir simplement échangé l'index 10. Quel est l'algorithme le plus efficace - c'est-à-dire celui qui utilise le moins de mouvements totaux?

MaiaVictor
la source
13
Pas si étrange, c'est une machine physique qui triera une liste de chariots collés sur une bande de looong enroulée. La machine ne peut que déplacer la bande vers l'avant ou vers l'arrière et ne peut échanger que les cartes voisines, bien sûr. Dans le monde réel, vous ne pouvez pas vous téléporter, alors, ce sont les restrictions ...
MaiaVictor
2
Donc, lorsque vous dites que vous voulez un algorithme qui n'alloue aucun octet autre que la taille du tableau , je suppose que vous ne faites référence qu'au stockage d'éléments, non? Je peux encore allouer des compteurs et autres?
Darkhogg
5
Oh, bien sûr. Bien sûr. Vous pouvez allouer des structures supplémentaires. Vous pouvez même allouer l'ensemble du tableau et faire beaucoup de calculs très lourds et cela vaut 0 coût. La seule chose que vous devez minimiser est le nombre de SWAP / MOVE de la machine physique réelle, car il est lent. Le tri à bulles est le meilleur que j'ai pu trouver, mais je suppose qu'il devrait y avoir de meilleures options.
MaiaVictor
1
Je ne pense pas qu'il existe un tel algorithme. Sans une mémoire supplémentaire, vous aurez aucun moyen de stocker tout état de contrôle.
Raphael
1
@svrm: oui, alors avec une RAM illimitée et la possibilité de copier la bande dans la RAM et de faire des calculs arbitraires gratuitement, l'algorithme "tout essayer et appliquer le meilleur" est optimal en termes de nombre de mouvements de la bande. Il est peu probable que ce soit pratique, mais c'est parce que dans la pratique, le temps d'exécution serait de plusieurs milliards d'années, pas 0 ;-) Si cela coûte N se déplace pour copier une bande de longueur N dans la RAM, alors la force brute naïve peut ne pas être optimale mais elle est dans N optimale. Mais rien de tout cela n'est spécifique à votre problème: de nombreux problèmes peuvent être résolus "hors ligne" en utilisant un algorithme bidon.
Steve Jessop

Réponses:

13

Considérez le tri du shaker à cocktail , qui est une version bidirectionnelle du tri à bulles. Vous faites des bulles de bas en haut, puis (c'est la partie ajoutée) vous faites des bulles de haut en bas, répétez jusqu'à ce que cela soit fait. C'est toujours , mais cela fait beaucoup moins de passes en moyenne, car les petits éléments près de l'extrémité supérieure du tableau seront déplacés vers leur position finale en une seule passe plutôt que N passes. De plus, vous pouvez garder une trace des positions les plus basses et les plus hautes où un swap s'est produit; les passes suivantes n'ont pas besoin de balayer au-delà de ces points.O(n2)

zwol
la source
4

Le nombre de swaps d'éléments adjacents nécessaires pour commander un tableau est égal au nombre d'inversions dans le tableau. Avec n éléments au total, il y a au plus n * (n-1) / 2 inversions, donc le tri par bulles donne le nombre asymptotiquement optimal de swaps dans ce modèle.

Charles
la source
En fait, le tri à bulles donnera exactement le nombre optimal de swaps. Cependant, pour chaque permutation, il existe plusieurs façons de faire le nombre optimal de swaps, et il n'est pas évident de savoir lequel réduit le nombre total de mouvements. (Par tri à bulles, je veux dire "choisissez le plus grand non trié et déplacez-le à la fin du tri")
Peter Kravchuk
4

O(n2)

-+

L'algorithme qui n'utilise pas d'indicateur booléen pour savoir si nous avons échangé un élément ou non, est donné ci-dessous (l'astuce pour conserver les informations dans l'état de la machine, plutôt que dans la mémoire):

Start:
    Do until we are not at the leftmost position (Op 4)
        move left (Op 2b)

Check:
    If we are at rightmost position (Op 3)
        goto Finished:
    If current value is larger than next value (Op 5)
        goto Unfinished:
    move right (Op 2a)
    Repeat Check:

Unfinished:
    If we are at rightmost position (Op 3)
        goto Start:
    If current value is larger than next value (Op 5)
        swap the elements (Op 1) and move right (Op 2a)
    Repeat Unfinished:

Finished:
    The list is sorted now, output it.

La solution d'Eric Lippert, le tri gnome fonctionne également, car il s'agit essentiellement d'un tri à bulles bidirectionnel.

Shreesh
la source
Qu'en est-il du tri par insertion?
Darkhogg
Le tri à bulles nécessite au moins deux compteurs de boucles qui sont déjà plus que permis.
Raphael
1
Non, vous pouvez aller à gauche et à droite, puis de droite à gauche, jusqu'à ce qu'il n'y ait pas de changement (maximum n fois) sans utiliser le compteur. Vous n'avez même pas besoin d'espace supplémentaire pour qu'un drapeau booléen note s'il y a un changement. S'il y a un changement, vous passez simplement à un autre sous-programme qui fait de même, sauf qu'il s'agit d'un autre sous-programme.
Shreesh
1
Et bien sûr, je suppose que vous pouvez lire en blanc aux deux extrémités afin que vous sachiez qu'il s'agit du début ou de la fin de la liste. Aussi, je suppose que nous lisons à la fois l'élément actuel et l'élément suivant pour savoir si nous devons échanger.
Shreesh
1
Ou, si nous modifions le swap d'opérateur en "swap sinon dans l'ordre croissant".
Shreesh