AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Iteratives Mergesort mit Stackemulation

Ein Thema von Delphi-Laie · begonnen am 15. Apr 2011 · letzter Beitrag vom 17. Mär 2016
Antwort Antwort
Delphi-Laie

Registriert seit: 25. Nov 2005
1.474 Beiträge
 
Delphi 10.1 Berlin Starter
 
#1

Iteratives Mergesort mit Stackemulation

  Alt 15. Apr 2011, 22:05
Beim einfachen Mergesort störte mich dessen von der Rekursion verursachte Stackbeanspruchung, und ich versuchte mithin, diese Rekursion bzw. Stackbeanspruchung zu eliminieren bzw. zu emulieren (oder zu ersetzen), und zwar mit einem schnöden Array. Mit der in R. Sedgewicks Standardwälzer vorgeschlagenen Methode, beispielhaft am Quicksort, kam ich einfach nicht weiter. Vielleicht liegt es daran, daß beim Quicksort bei der Abwärtsbewegung der Rekursion sortiert wird (top-down?), beim Mergesort hingegen erst bei der Aufwärtsbewegung derselben (bottom-up?). Wie es genau bezeichnet wird, ist mir nicht bekannt. Jedenfalls tat ich mich daran, auch für diesen Sortieralgorithmus eine stackbefreite Variante zu generieren, was mir auch nach knapp 2 Tagen endlich gelang. Dabei ist der zweite Aufruf der beiden Rekursivaufrufe

Delphi-Quellcode:
if mitte>links then mergesort(links,mitte);
if rechts>succ(mitte) then mergesort(succ(mitte),rechts);
nicht mehr offensichtlich erkennbar.

Wohlgemerkt: Es geht hier nicht um das von Natur aus iterative sog. Natural Mergesort, bei dem anfängliche vorhandene sortiert vorliegende Teilmengen/-folgen gesucht und gefunden werden und der Algorithmus schrittweise „nach oben“ auf diesen aufbaut. Ebensowenig bedarf die iterative Stackemulation, daß die Anzahl der zu sortierenden Elemente eine Zweierpotenz sein muß (derartige Mergesortvarianten gibt es auch), denn das ist beim gewöhnlichen rekursiven Mergesort ja auch nicht der Fall.

Ich fand dazu im Internet jedenfalls nichts.

Kurzum, hier das nunmehr anscheinend fehlerfreie Resultat:

Delphi-Quellcode:
procedure mergesort(links,rechts:word);
var
stack:array {[0..x]} of byte;{oder word oder integer oder cardinal o.ä,
statisch oder dynamisch zu deklarieren und natürlich auch ausreichend zu dimensionieren...}

mitte,richtung,stackzaehler:word;//Richtung: 0 bedeutet ab-, 1 bedeutet aufwärts
begin
  richtung:=0; //Richtung 0 bedeutet Ab-, Richtung 1 Aufwärtsbewegung
  stackzaehler:=2;
  stack[1]:=0; //nötig wegen der Befehlszeile unten: rechts:=stack[pred(stackzaehler)]
  repeat
  mitte:=(links+rechts) div 2;
  if (richtung=0) and (mitte>links) then //neue Intervallgrenzen ermitteln
    begin
    //mergesort(links,mitte);
    stack[stackzaehler]:=links;
    stack[succ(stackzaehler)]:=rechts;
    inc(stackzaehler,2);
    rechts:=mitte
    end;

  if ((richtung=0) and ((mitte=links)) and (succ(mitte)<=rechts)) //Sortierung 2er einzelner Elemente
  or (richtung=1) //Sortierung durch Mischen sortierter Teilmengen
  then
     begin
     {Hier erfolgt das Verschmelzen („Mergen“) der beiden schon sortierten Teilmengen
      links - mitte und succ(mitte) - rechts
      mit einem Algorithmus beliebiger Wahl (z.B. in meinem Sortierkino}

     end;

    if (links=mitte) or (richtung=1) then
      begin
      if rechts<stack[pred(stackzaehler)] then
        begin //nach „rechts“ hinübergehen
        richtung:=0;
        links:=succ(rechts);
        rechts:=stack[pred(stackzaehler)]
        end
      else
        begin //wieder „auftauchen“
        richtung:=1;
        dec(stackzaehler,2);
        links:=stack[stackzaehler];
        rechts:=stack[succ(stackzaehler)]
        end
      end
  until stackzaehler=0
end;
Edit [17.04.2011, 15:20]: Code geändert

Geändert von Matze (17. Apr 2011 um 20:50 Uhr)
  Mit Zitat antworten Zitat
FredlFesl

Registriert seit: 19. Apr 2011
293 Beiträge
 
Delphi 2009 Enterprise
 
#2

AW: Iteratives Mergesort mit Stackemulation

  Alt 19. Apr 2011, 21:22
Wo ist der Vorteil ggü. der rekursiven Version, außer das es schlechter lesbar ist?
  Mit Zitat antworten Zitat
Namenloser

Registriert seit: 7. Jun 2006
Ort: Karlsruhe
3.724 Beiträge
 
FreePascal / Lazarus
 
#3

AW: Iteratives Mergesort mit Stackemulation

  Alt 19. Apr 2011, 21:37
Wo ist der Vorteil ggü. der rekursiven Version, außer das es schlechter lesbar ist?
Kein Stackoverflow bei großen Datenmengen.
  Mit Zitat antworten Zitat
Satty67

Registriert seit: 24. Feb 2007
Ort: Baden
1.566 Beiträge
 
Delphi 2007 Professional
 
#4

AW: Iteratives Mergesort mit Stackemulation

  Alt 19. Apr 2011, 22:07
Wie stark wird den der Stack belastet? Ich bin mir MergeSort nicht richtig vertraut.

(Ist das zu OT oder hier erlaubt: Bei welcher Anforderung ist MergeSort noch ideal? Meine das MergeSort sich in jedem Gebiet einem besser geeigneten Algorithmus geschlagen geben muss, weshalb ich mich bisher auch kaum damit beschäftigt hatte. Ist keine eigene Erfahrung, nur "angelesen").

Geändert von Satty67 (19. Apr 2011 um 22:17 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von JasonDX
JasonDX
(CodeLib-Manager)

Registriert seit: 5. Aug 2004
Ort: München
1.062 Beiträge
 
#5

AW: Iteratives Mergesort mit Stackemulation

  Alt 19. Apr 2011, 22:58
Wo ist der Vorteil ggü. der rekursiven Version, außer das es schlechter lesbar ist?
Kein Stackoverflow bei großen Datenmengen.
Bei welchen Großen Datenmengen erwartest du denn einen Stackoverflow? Ich schätze mal, 512 rekursive Aufrufe sollten noch gehn. Die Tiefe bei Mergesort für eine Menge der Mächtigkeit n liegt bei ld(n), folglich, um nicht 512 rekursive Aufrufe zu überschreiten, darf die zu Sortierende Menge nicht mehr als 2^512 Elemente enthalten. Wenn sich mein Kopf nicht verrechnet hat, sind das ca. 10^150 Elemente, ab denen die Rekursionstiefe von 512 erreicht werden würde.

greetz
Mike
Mike
Passion is no replacement for reason
  Mit Zitat antworten Zitat
Delphi-Laie

Registriert seit: 25. Nov 2005
1.474 Beiträge
 
Delphi 10.1 Berlin Starter
 
#6

AW: Iteratives Mergesort mit Stackemulation

  Alt 20. Apr 2011, 10:06
Eine Grundsatzdiskussion wollte und erwartete ich eigentlich nicht.

Es gibt Leute, die die Rekursion nicht mögen, auch wenn die kürzeren Quelltexte natürlich übersichtlicher (und nicht etwa schlecher lesbar, das ist etwas anderes) und damit weniger fehleranfällig sind.

Hinzu kommt, daß man jegliches Risiko des Stacküberlaufes ausschließt - auch wenn man den Stack maximiert, kann man sich bei ihm eigentlich nie sicher sein, daß er - insbesondere, weil auch andere Routinen darauf zugreifen könnten - doch überläuft. Dimensioniert man den Hilfs-/Pseudostack (Array) hingegen dynamisch während der Laufzeit des Sortierens bei Bedarfe neu, so sind Speicherüberläufe praktisch ausgeschlossen.

Letztlich ging es hier (und mir) nur darum, zu zeigen, wie man die Rekursion auch des klassischen Mergesorts beseitigt bekommt (bzw. bekommen kann).

Wie stark wird den der Stack belastet? Ich bin mir MergeSort nicht richtig vertraut.

(Ist das zu OT oder hier erlaubt: Bei welcher Anforderung ist MergeSort noch ideal? Meine das MergeSort sich in jedem Gebiet einem besser geeigneten Algorithmus geschlagen geben muss, weshalb ich mich bisher auch kaum damit beschäftigt hatte. Ist keine eigene Erfahrung, nur "angelesen").
Nein, Mergesort muß sich ganz und gar nicht geschlagen geben. Mergesort hat in jedem Falle nur eine n*log(n)-Komplexität/Laufzeit (im Gegensatz zu Quicksort) und kann zudem stabil implementiert werden (im Gegensatz zu Quick- und Heapsort). Der einzige verbliebene Nachteil, daß es zusätzlichen Speicher zum Verschmelzen der schon sortierten Teilmengen benötigt, stimmt auch nicht mehr, denn auch dafür gibt es Lösungen (die ich in meinem Sortierprogramm einfügte).
  Mit Zitat antworten Zitat
FredlFesl

Registriert seit: 19. Apr 2011
293 Beiträge
 
Delphi 2009 Enterprise
 
#7

AW: Iteratives Mergesort mit Stackemulation

  Alt 21. Apr 2011, 20:22
Mergesort zeichnet sich durch die sehr geringe Anzahl an Datenbewegungen aus. Mit diesem Sortierverfahren lassen sich Dateien sehr effizient sortieren (record by record).
  Mit Zitat antworten Zitat
Delphi-Laie

Registriert seit: 25. Nov 2005
1.474 Beiträge
 
Delphi 10.1 Berlin Starter
 
#8

AW: Iteratives Mergesort mit Stackemulation

  Alt 15. Mär 2016, 16:01
Der Vollständigkeit halber möchte ich noch eine Alternativversion des rekursionsfreien Mergesorts anbieten, die zum einen für ziemlich viele "Entrekursivierungen" geeignet zu sein scheint (bei Endrekursionen geht es allerdings auch einfacher, und bei verschachtelten dürfte es scheitern) und zum anderen sich eines etwas ausgereifteren Stackemulators bedient (auf der Grundlage von Roberts Sedgewicks Buch "Algorithmen"):

Delphi-Quellcode:
type link=^node;
 node=record
   key:word;
   next:link
   end;

  TStackemulator=class
  head,z:link;
  constructor create(a:word);
  destructor destroy;override;
  procedure push(v:word);
  function pop:word;
  function isempty(autodelete:boolean):boolean;
  procedure pop3(var a,b,c:word);
  procedure push3(a,b,c:word);
  end;

constructor TStackemulator.create(a:word);
var l:word;
begin
new(head);
new(z);
head^.next:=z;
z^.next:=z;
for l:=1 to a do push(0)
end;

destructor TStackemulator.destroy;
begin
//inherited//funktioniert auch hier und sogar mit beiden inherited
z^.next:=nil;
head^.next:=nil;
z:=nil;
head:=nil;
dispose(z);
dispose(head);
inherited
end;

procedure TStackemulator.push(v:word);
var t:link;
begin
new(t);
t^.key:=v;
t^.next:=head^.next;
head^.next:=t
end;

function TStackemulator.pop:word;
var t:link;
begin
t:=head^.next;
result:=t^.key;
head^.next:=t^.next;
dispose(t)
end;

function TStackemulator.isempty(autodelete:boolean):boolean;
begin
result:=head^.next=z;
if result and autodelete then destroy
end;

procedure TStackemulator.push3(a,b,c:word);
begin
push(a);
push(b);
push(c)
end;

procedure TStackemulator.pop3(var a,b,c:word);
begin
c:=pop;
b:=pop;
a:=pop
end;

procedure mergesort(links,rechts:word);
label 1,2;
var mitte,Position:word;
Stackemulator:TStackemulator;
begin
Stackemulator:=TStackemulator.create(3);//mit 3 Leerelementen ("0") füllen, da (bei diesem Algorithmus) immer 3 Elemente abgelegt und abgerufen werden
  repeat
  mitte:=(links+rechts) div 2;
  if links<mitte then
    begin
    Stackemulator.push3(links,rechts,1);
    rechts:=mitte;
    continue
    end;
  1:if succ(mitte)<rechts then
    begin
    Stackemulator.push3(links,rechts,2);
    links:=succ(mitte);
    continue
    end;
  2:merge(links,mitte,rechts);//hier einen beliebigen Mergealgorithmus einfügen bzw. ausführen, 1. Teilarray: von links bis mitte, 2. Teilarrary: von mitte+1 bis rechts
  Stackemulator.pop3(links,rechts,Position);
  mitte:=(links+rechts) div 2;
  case Position of
    1:goto 1;
    2:goto 2
    end
  until Stackemulator.isempty(true)//bei Leersein automatisch löschen
end;

Geändert von Daniel (22. Mär 2016 um 19:23 Uhr) Grund: Code auf und nach Wunsch des Autors aktualisiert.
  Mit Zitat antworten Zitat
Benmik

Registriert seit: 11. Apr 2009
542 Beiträge
 
Delphi 11 Alexandria
 
#9

AW: Iteratives Mergesort mit Stackemulation

  Alt 17. Mär 2016, 12:40
Die verlinkte Webseite ("Sortierkino") ist schon beeindruckend. Man hat schon geahnt, dass es noch so einiges jenseits von Quick- und Bubblesort gibt, aber das ist wirklich eine erschlagende Fülle. Ich hielt Quicksort für den Allgemeingebrauch immer für den Algorithmus der Wahl (und fühlte mich dadurch bestätigt, dass Delphi ihn ja auch für Binary Search nimmt), aber das muss man vielleicht überdenken.
  Mit Zitat antworten Zitat
Delphi-Laie

Registriert seit: 25. Nov 2005
1.474 Beiträge
 
Delphi 10.1 Berlin Starter
 
#10

AW: Iteratives Mergesort mit Stackemulation

  Alt 17. Mär 2016, 15:53
Danke für die Anerkennung!

Die verlinkte Webseite ("Sortierkino") ist schon beeindruckend. Man hat schon geahnt, dass es noch so einiges jenseits von Quick- und Bubblesort gibt, aber das ist wirklich eine erschlagende Fülle.
Nunja, viele der in meinem Programm angebotenen Algorithmen sind nur eine Modifikation bekannter Algorithmen, die ich aber aus Demonstrationsgründen wert erachtete, als separaten Eintrag mit aufzunehmen / -führen.

Ich hielt Quicksort für den Allgemeingebrauch immer für den Algorithmus der Wahl (und fühlte mich dadurch bestätigt, dass Delphi ihn ja auch für Binary Search nimmt), aber das muss man vielleicht überdenken.
Für die Erstsortierung - dann logischerweise unsortierter - Daten trifft das auch zu, dafür ist Quicksort wirklich die Wahl ohne Qual, weil es der schnellste der In-Situ-Algorithmen ist. Bei bereits sortierten Daten macht sich jedoch die Nichstabilität des Quicksorts unangenehm bemerkbar. Außerdem droht der Algorithmus dann bei bestimmten Strukuturen der Eingangsmenge zu entarten (quadratische Komplexität). Es gibt natürlich auch Lösungen, die die Wahrscheinlichkeit dafür auf ein Minimum reduzieren (oder ganz beseitgen?), so z.B. das 3-Wege-Quicksort. Weiterer Aspekt: Quicksort ist mit akzeptablem Aufwand auch für mittelmäßig routinierte Programmierer zu implementieren.

Geändert von Delphi-Laie (17. Mär 2016 um 21:26 Uhr)
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 06:08 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz