Was ist der effizienteste Konstantraum-Sortieralgorithmus?

19

Ich suche nach einem Sortieralgorithmus für int-Arrays, der kein anderes Byte als die Größe des Arrays zuweist und auf zwei Befehle beschränkt ist:

  1. SWAP: Tauschen Sie den nächsten Index gegen den aktuellen aus.

  2. MOVE: bewegt den Cursor zum Index +1 oder -1;

Das heißt, Sie können weder nicht benachbarte Indizes tauschen, noch den Index tauschen 100, nachdem Sie gerade den Index getauscht haben 10. Was ist der effizienteste Algorithmus - dh derjenige, der die geringere Menge an Gesamtbewegungen verwendet?

MaiaVictor
quelle
13
Nicht so seltsam, es ist eine physische Maschine, die eine Liste von Wagen sortiert, die auf ein langes, aufgerolltes Band geklebt sind. Das Gerät kann das Band nur vorwärts oder rückwärts bewegen und nur benachbarte Karten austauschen. In der realen Welt kann man nicht herum teleportieren, das sind also die Einschränkungen ...
MaiaVictor
2
Wenn Sie also sagen, Sie möchten einen Algorithmus, der kein anderes Byte als die Größe des Arrays zuweist, dann beziehen Sie sich wohl nur auf den Elementspeicher, oder? Kann ich noch zähler vergeben und so?
Darkhogg
5
Oh sicher. Na sicher. Sie können einige zusätzliche Strukturen zuordnen. Sie können sogar das gesamte Array zuweisen und eine Menge wirklich schwerer Berechnungen durchführen, und das zählt als 0 Kosten. Das einzige, was Sie minimieren müssen, ist die Anzahl der SWAP / MOVEs der tatsächlichen physischen Maschine, da diese langsam ist. Die Bubble-Sortierung ist die beste, die ich mir vorstellen konnte, aber ich schätze, dass es bessere Optionen geben sollte.
MaiaVictor
1
Ich glaube nicht, dass es einen solchen Algorithmus gibt. Ohne jeden zusätzlichen Speicher, haben Sie keine Möglichkeit , jeden Steuerzustand zu speichern.
Raphael
1
@svrm: Ja, mit unbegrenztem RAM und der Möglichkeit, das Band in den RAM zu kopieren und frei zu berechnen, ist der Algorithmus "alles versuchen und das Beste anwenden" in Bezug auf die Anzahl der Bandbewegungen optimal. Es ist unwahrscheinlich, dass es praktikabel ist, aber das liegt daran, dass die Laufzeit in der Praxis Milliarden von Jahren und nicht 0 beträgt ;-) Wenn das Kopieren eines Bandes der Länge N in den Arbeitsspeicher N-Züge kostet, ist naive Brute Force möglicherweise nicht optimal, liegt aber innerhalb von N von optimal. Aber nichts davon ist spezifisch für Ihr Problem: Viele Probleme, wenn sie auf diese Weise angegeben werden, könnten "offline" unter Verwendung eines falschen Algorithmus gelöst werden.
Steve Jessop

Antworten:

13

Betrachten Sie Cocktail-Shaker-Sorte , die eine bidirektionale Version der Bubble-Sorte ist. Sie sprudeln von niedrig nach hoch und dann (dies ist der hinzugefügte Teil) sprudeln Sie von hoch nach niedrig. Wiederholen Sie dies, bis Sie fertig sind. Dies ist immer noch , aber es werden im Durchschnitt deutlich weniger Durchgänge ausgeführt, da kleine Elemente nahe dem oberen Ende des Arrays in einem einzigen Durchgang anstelle von N Durchgängen an ihre endgültige Position verschoben werden. Außerdem können Sie die niedrigsten und höchsten Positionen verfolgen, an denen ein Swap stattgefunden hat. nachfolgende Durchläufe müssen nicht über diese Punkte hinaus gescannt werden.O(n2)

zwol
quelle
4

Die Anzahl der Auslagerungen benachbarter Elemente, die zum Ordnen eines Arrays erforderlich sind, entspricht der Anzahl der Inversionen im Array. Mit insgesamt n Elementen gibt es höchstens n * (n-1) / 2 Inversionen, so dass die Blasensortierung die asymptotisch optimale Anzahl von Swaps in diesem Modell ergibt.

Charles
quelle
Tatsächlich ergibt die Blasensortierung genau die optimale Anzahl von Swaps. Für jede Permutation gibt es jedoch mehrere Möglichkeiten, die optimale Anzahl von Swaps durchzuführen, und es ist nicht offensichtlich, welche die Gesamtanzahl von Zügen verringert. (Mit Blasensortierung meine ich "wähle das größte nicht sortierte und verschiebe es ans Ende des sortierten")
Peter
4

Der einzige Algorithmus mit den beiden von Ihnen genannten Operatoren, der sehr effizient ist, ist die Blasensortierung. Die Komplexität des Algorithmus ist im schlimmsten Fall .O(n2)

Ich gehe auch davon aus, dass wir neben den beiden Operationen auch überprüfen können, ob wir am rechten (Op 3) oder am linken Ende (Op 4) sind, entweder mithilfe der Sentinels und oder durch eine Operation in der Liste . Wir sollten auch eine Vergleichsoperation (op. 5) separat geben oder mit einer Swap-Operation kombinieren. Wenn die Vergleichsoperation mit der Swap-Operation kombiniert ist, muss sie uns mitteilen, ob der Swap durchgeführt wurde oder nicht.+ +

Der Algorithmus, der kein Boolesches Flag verwendet, um festzustellen, ob ein Element vertauscht wurde, wird im Folgenden beschrieben (der Trick, die Informationen im Status der Maschine und nicht im Speicher zu belassen):

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.

Die Lösung von Eric Lippert, der Gnomensorte, funktioniert auch, weil es sich im Grunde um eine Zwei-Wege-Blasensorte handelt.

Shreesh
quelle
Was ist mit Einfügungsart?
Darkhogg
Die Blasensortierung benötigt mindestens zwei Schleifenzähler, die bereits mehr als zulässig sind.
Raphael
1
Nein, Sie können nach links und rechts und dann von rechts nach links gehen, bis keine Änderung mehr erfolgt (maximal n-mal), ohne den Zähler zu verwenden. Sie brauchen nicht einmal zusätzlichen Platz, damit eine Boolesche Flagge notiert, wenn sich etwas ändert. Bei einer Änderung springen Sie einfach zu einer anderen Unterroutine, die dasselbe tut, mit der Ausnahme, dass es sich um eine andere Unterroutine handelt.
Shreesh
1
Und natürlich nehme ich an, dass Sie an beiden Enden leer lesen können, damit Sie wissen, dass es am Anfang oder Ende der Liste steht. Außerdem gehe ich davon aus, dass wir sowohl das aktuelle als auch das nächste Element lesen, um zu wissen, ob wir tauschen müssen.
Shreesh
1
Oder wenn wir den Operator-Swap in "Swap, wenn nicht in aufsteigender Reihenfolge" ändern.
Shreesh