Für das Mischen braucht man möglichst lange sortierte Teilsequenzen. Die maximale Durchschnittlänge von ca. 2n, die in einer Reihung mit gegebener Länge n erreicht werden kann, ergibt folgende Strategie:
generic type TElement is private; -- type TElement is range <>; -- nur fuer Testausgabe with function "<" (Links, Rechts: TElement) return Boolean; with procedure Lesen (X: out TElement); -- vom Eingabeband with function Ende return Boolean; -- end of file des Eingabebandes with procedure Schreiben (X: TElement); -- auf das Ausgabeband with procedure Ende_der_Sequenz; -- Signal nach aussen am Ende jeder Sequenz procedure Sequenzen_erzeugen (Groesse: in Positive); -- erzeugt sortierte Sequenzen mit Hilfe zweier Haldenkanaele
procedure Sequenzen_erzeugen (Groesse: in Positive) is -- Groesse des Speicherplaztes subtype TIndex is Natural range 0 .. Groesse + 1; -- erster und letzter Platz technisch bedingt type TReihung is array (TIndex range 1 .. Groesse) of TElement; Halde: TReihung; Element: TElement; -- das aktuelle Element Letztes: TElement; -- das letzte geschriebene Element Erstes: Boolean := True; -- noch kein Element wurde geschrieben Akt_Anfang: TIndex range Halde'Range := Halde'Last; -- Anfang der aktiven Halde Akt_Ende: TIndex := Halde'Last-1; -- Ende der aktiven Halde Ers_Anfang: TIndex range Halde'Range := Halde'Last; -- Anfang der Ersatzhalde Ers_Ende: TIndex := Halde'Last-1; -- Ende der Ersatzhalde -- Halde leer wenn Anfang > Ende function Akt_Leer return Boolean is -- aktive Halde leer begin return Akt_Anfang > Akt_Ende; end Akt_Leer; function Ers_Leer return Boolean is -- Ersatzhalde leer begin return Ers_Anfang > Ers_Ende; end Ers_Leer; function Halde_Voll (Anfang: TIndex) return Boolean is -- aktive oder Ersatzhalde (mit Anfang) voll begin return Anfang = Halde'First; end Halde_Voll; function Halde_Leer (Anfang, Ende: TIndex) return Boolean is -- nur fuer pre- und postcondition begin return Anfang > Ende; end Halde_Leer; function Ist_Halde (Anfang, Ende: in TIndex) return Boolean is -- nur fuer pre- und postcondition begin -- die for-Schleife erhoeht die Zeitkomplexitaet von n log n auf n**2; sie soll im Produkt auskommentiert werden: for I in Anfang .. Ende / 2 loop if Halde (2 * I) < Halde (I) then return False; end if; end loop; return True; end Ist_Halde;
procedure Senken (Links, Rechts: in TIndex) is -- Halde (Links .. Rechts) wird durch das Senken des Elements Halde (Links - 1) zur Halde (Links - 1 .. Rechts) erweitert. Index: TIndex range Halde'Range := Links - 1; -- linke Grenze der neuen Halde Nachfolger: TIndex'Base range Halde'First .. 2 * Halde'Last := 2 * Index; Element_zu_senken: constant TElement := Halde (Index); -- neues Element begin -- precondition Links > Halde'First and Ist_Halde (Links, Rechts) loop -- variant Nachfolger > old Nachfolger exit when Nachfolger > Rechts; -- weil Nachfolger nicht existiert if Nachfolger < Rechts and then Halde (Nachfolger + 1) < Halde (Nachfolger) then -- groesseren Nachfolger oder Nachfolger+1 auswaehlen Nachfolger := Nachfolger + 1; end if; exit when Element_zu_senken < Halde (Nachfolger); -- Platz gefunden Halde (Index) := Halde (Nachfolger); -- hochruecken Index := Nachfolger; -- runtergehen Nachfolger := 2 * Index; -- linker Nachfolger, wenn existiert end loop; Halde (Index) := Element_zu_senken; -- Element einfuegen -- postcondition Ist_Halde (Links - 1, Rechts) end Senken;
procedure Fuellen (Anfang, Ende: in out TIndex; Element: in TElement) is begin -- fuegt Element in die Halde (Anfang .. Ende) ein, indem sie entweder zu Halde (Anfang - 1 .. Ende) verlaengert oder das Spitzenelement ausgegeben wird if Halde_voll (Anfang) then -- Schreiben (Halde (Anfang)); else -- Halde nicht voll Anfang := Anfang - 1; -- nach vorne verlaengern end if; Halde (Anfang) := Element; -- neues Element eintragen Senken (Anfang + 1, Ende); end Fuellen;
procedure Entleeren (Anfang, Ende: in out TIndex) is begin -- gibt das Element Halde (Anfang) aus und erzeugt die Halde (Anfang .. Ende - 1), indem Halde (Ende) gesenkt wird -- precondition not Halde_Leer (Anfang, Ende) and Ist_Halde (Anfang + 1, Ende) -- nicht nach Haldenwechsel: precondition Ist_Halde (Anfang, Ende) Schreiben (Halde (Anfang)); Halde (Anfang) := Halde (Ende) ; -- hinteres Element Ende := Ende - 1; -- Halde hinten verkuerzen Senken (Anfang + 1, Ende); -- in der verkuerzten Halde -- postcondition Ist_Halde (Anfang, Ende); end Entleeren;
begin -- Sequenzen_erzeugen while not Ende loop -- variant muss vom Benutzer durch Lesen gesichert werden Lesen (Element); -- Rueckruf if not Halde_voll (Akt_Anfang) then -- Halde noch nicht voll, aufbauen Fuellen (Akt_Anfang, Akt_Ende, Element); elsif Erstes or else not (Element < Letztes) then -- Element gehoert dieser Sequenz Erstes := False; if Element < Halde (Akt_Anfang) then -- sofort ausgeben Schreiben (Element); Letztes := Element; else -- wurde schon kleineres ausgegeben Letztes := Halde (Akt_Anfang); -- Element wird ausgegeben Fuellen (Akt_Anfang, Akt_Ende, Element); -- Fuellen ruft Schreiben auf, wenn Halde voll end if; -- nicht sofort ausgeben else -- Element gehoert der naechsten Sequenz, in die Ersatzhalde: Letztes := Halde (Akt_Anfang); -- Element wird ausgegeben if Akt_Leer then -- aktive Halde leer Ende_der_Sequenz; -- Signal nach aussen -- Halden tauschen: Akt_Ende := Ers_Ende; -- Ersatzhalde wird die aktive -- Akt_Anfang = ? Ers_Anfang := Ers_Ende + 1; -- neue Ersatzhalde wird leer -- Ers_Ende = ? end if; -- aktive Halde leer Entleeren (Akt_Anfang, Akt_Ende); -- in der Halde wird Platz gemacht: Entleeren ruft Schreiben auf und verkuerzt die aktive Halde Fuellen (Ers_Anfang, Ers_Ende, Element); -- die Ersatzhalde ist nie voll end if; -- naechste Sequenz end loop; -- Leseschleife
-- alle Eingabedaten wurden eingelesen; zuerst aktive Halde abbauen: while not Akt_Leer loop -- variant Akt_Ende < old Akt_Ende Entleeren (Akt_Anfang, Akt_Ende); end loop;
-- Ersatzhalde abbauen: Ende_der_Sequenz; while not Ers_Leer loop -- variant Ers_Ende < old Ers_Ende Entleeren (Ers_Anfang, Ers_Ende); end loop; Ende_der_Sequenz; end Sequenzen_erzeugen;
with Text_IO, Sequenzen_erzeugen; procedure Seq_Test is -- Testprogramm fuer das Paket Sequenzen_erzeugen type TDaten is new Integer; procedure Uebergeben (Daten: out TDaten); function Datenende return Boolean; procedure Uebernehmen (Daten: TDaten); procedure Umschalten; -- auf die naechste Sequenz
procedure Ganzzahl_Sequenzen_erzeugen is new Sequenzen_erzeugen ( TElement => TDaten, "<" => "<", -- fuer TDaten = Integer Lesen => Uebergeben, Ende => Datenende, Schreiben => Uebernehmen, Ende_der_Sequenz => Umschalten);
type UEingabedaten is array (Positive range <>) of TDaten; Eingabedaten: constant UEingabedaten := (13, -45, 32, -5, 22, 80, -32, 123, -65, 27, -23, 35, 37, -88, -77, -13, 37, 273, 35, 77, 16, -35, 62, 58, -22, 47, -62, 126, 65, 21, -26, 55, 61, 88, -11, -16, 61, 216, 65, 11, -21, 15, 17, -44, -27, -11, 17, 271, 15, 97, 19, -15, 66, 54, 22, -47, 52, -126, 35, 26, -16, 75, 68, 44, 11, 56, -61, -216, 95, 91, 24, 15, 611, 188, 151, -106, 651, 266, -65, 111, 21, -105, 157, -344, -127, 11, 107, 275, 151, 297, 323, 38, 37, 344, 327, 333, 37, 273, 38, 97, 109, -15, 56, 154, -22, 417, 5, 126, 35, 206, 39, 338, 66, 84, 22, 347, 82, 26, 38, 26, 336, 78, 68, 44, 33, 86, 363, 236, 98, 93, 32, 40, 70, 10); Index: Natural range Eingabedaten'First - 1 .. Eingabedaten'Last := Eingabedaten'First - 1; Sequenz_Zahl: Natural := 0; Speicherplatzgroesse: Positive range 2 .. Positive'Last; -- wird eingelesen
procedure Uebergeben (Daten: out TDaten) is begin -- variant Index > old Index Index := Index + 1; Daten := Eingabedaten (Index); exception when Constraint_Error => Text_IO.Put_Line ("Lesefehler: Eingabedaten sind zu Ende"); end Uebergeben; function Datenende return Boolean is begin return Index = Eingabedaten'Last; end Datenende; procedure Uebernehmen (Daten: TDaten) is begin Text_IO.Put (TDaten'Image (Daten) & ", "); end Uebernehmen; procedure Umschalten is begin Sequenz_Zahl := Sequenz_Zahl + 1; Text_IO.Put_Line ("#"); -- "#" signalisiert Sequenzwechsel end Umschalten;
package Int_IO is new Text_IO.Integer_IO (Integer); begin -- Seq_Test Text_IO.Put ("Sortierte Sequenzen der Testfolge mit der Laenge von " & Natural'Image (Eingabedaten'Length)); Text_IO.Put (" werden erzeugt. Bitte Speicherplatzgroesse (Ganzzahl > 1) eingeben: "); Int_IO.Get (Speicherplatzgroesse);
Ganzzahl_Sequenzen_erzeugen (Speicherplatzgroesse); -- mit Rueckrufen Text_IO.Put_Line ("##"); -- "##" signalisiert das Ende der letzten Sequenz Text_IO.Put (Natural'Image (Sequenz_Zahl) & " Sequenzen mit der Durchschnittslaenge von ca. "); Text_IO.Put_Line (Natural'Image (Eingabedaten'Length / Sequenz_Zahl) & " wurden erzeugt."); end Seq_Test;
Rückmeldungen bitte an den Autor solymosi@tfh-berlin.de
Leitseite des Autors