home *** CD-ROM | disk | FTP | other *** search
/ Turbo Toolbox / Turbo_Toolbox.iso / qpdemo / sortdemo.pas < prev    next >
Pascal/Delphi Source File  |  1989-07-21  |  28KB  |  905 lines

  1. PROGRAM sort_demo;
  2. {========================================================================
  3.  
  4.    QuickPascal Sortier-Demo
  5.    ------------------------
  6.    Dieses Programm demonstriert grafisch 6 gebräuchliche Sortier-
  7.    algorithmen. Es gibt horizontale Balken aus, die alle eine unter-
  8.    schiedliche Länge haben, in zufälliger Reihenfolge vorliegen und
  9.    der Länge nach sortiert werden.
  10.  
  11.    Außerdem verwendet das Programm sound-Anweisungen, um abhängig von
  12.    der Position des auszugebenden Balkens unterschiedliche Tonhöhen zu
  13.    erzeugen. Beachten Sie, daß die sound-Anweisungen die Geschwindigkeit
  14.    jedes Sortieralgorithmus' verlangsamen, so daß Sie den Fortgang der
  15.    Sortierung verfolgen können. Daher sind die gezeigten Zeiten nur zum
  16.    Vergleich bestimmt. Sie sind keine genaue Messung der Sortier-
  17.    geschwindigkeit.
  18.  
  19.    HINWEIS: Betätigen Sie STRG+UNTBR während des Sortierens innerhalb
  20.    der QuickPascal-Umgebung, kann der Lautsprecher angeschaltet bleiben.
  21.    Um ihn auszustellen, setzen Sie sort_demo fort (F5 drücken), oder
  22.    gehen Sie zu einem Aufruf zu NoSound (F10 mehrfach drücken).
  23.  
  24.    Wenn Sie diese Sortierroutinen in eigenen Programmen verwenden,
  25.    werden Sie vielleicht Unterschiede in deren relativen Geschwindig-
  26.    keiten feststellen (zum Beispiel kann das Sortieren durch Austausch
  27.    schneller sein als der Shellsort), abhängig von der Anzahl der zu
  28.    sortierenden Elemente und wie "ungeordnet" diese zu Beginn des
  29.    Sortiervorgangs vorliegen.
  30.  
  31. }
  32. {$M 10000, 0, 0 }  { Heap wird nicht benutzt }
  33. {$B-}              { Boolsche Bewertung }
  34. {$R-}              { Bereichsüberprüfung aus }
  35.  
  36. USES
  37.     Crt, Dos;
  38.  
  39. CONST
  40.  
  41.     block      = #223;
  42.     esc        = #27;
  43.     null_zchn  = #0;
  44.     leer       = #32;
  45.  
  46.     ticks_pro_sek = 18.2;  { Uhrschlag pro Sekunde }
  47.  
  48.     maxmax_balk = 43; { Absolutes Maximum der Balkenanzahl }
  49.     max_sort   =  6;  { Anzahl der Sortieralgorithmen }
  50.  
  51.     { Menüdimensionen }
  52.     menu_top    =  1;
  53.     menu_links  = 49;
  54.     menu_hoehe  = 18;
  55.     menu_breite = 80 - menu_links;
  56.  
  57.     { Vorgabefarben, geändert für Monochrom }
  58.     bild_rueck     : Byte = Black;
  59.     menu_rueck     : Byte = LightGray;
  60.     menu_rahmen    : Byte = LightBlue;
  61.     menu_text      : Byte = Black;
  62.     menu_status    : Byte = LightCyan;
  63.     menu_hervorghb : Byte = Yellow;
  64.  
  65. TYPE
  66.     sort_art      = ( Einfuegen, Bubble, Heap, Austausch, Shell, Quick );
  67.     sort_range    = First( sort_art )..Last( sort_art );
  68.     sort_elemente = RECORD
  69.     lng    : Byte;     { Balkenlänge (des Sortierelements) }
  70.     Farbe  : Integer;  { Balkenfarbe }
  71.     END;
  72.     sort_arrays = ARRAY[1..maxmax_balk] OF sort_elemente;
  73.  
  74. VAR
  75.     { Array von Sortierprozeduren }
  76.     sort : ARRAY[sort_range] OF PROCEDURE;
  77.     sort_array,               { zu sortierende Elemente }
  78.     unsort_array              { Unsortierte Kopie des Arrays }
  79.     : sort_arrays;
  80.     max_balk,                 { Max. zu sortierende Balken }
  81.     max_Farben  : Integer;    { Max. unterstützte Farben }
  82.     start_zeit,               { Startzeit }
  83.     end_zeit    : LongInt;    { Endzeit }
  84.     pause       : Word;       { Länge der Pause }
  85.     start_modus : Word;       { Start-Videomodus }
  86.     ton_ein     : Boolean;    { Wahr, falls Ton ein }
  87.     Balken         : STRING[maxmax_balk]; { String der Balkenzeichen }
  88.  
  89.     { Tonleiter-Frequenz für die Anzahl der zu sortierenden Zeilen }
  90.     tonleiter_frequenz : Integer;
  91.  
  92. CONST
  93.     { Menüelemente }
  94.     menu : ARRAY[1..menu_hoehe] OF CSTRING[30] =
  95.     ( '  QuickPascal Sortier-Demo',
  96.       ' ',
  97.       'Einfügen',
  98.       'Bubble',
  99.       'Heap',
  100.       'Austausch',
  101.       'Shell',
  102.       'Quick',
  103.       ' ',
  104.       'Ton ein/aus: ',
  105.       ' ',
  106.       'Pause: ',
  107.       '<   (Langsamer)',
  108.       '>   (Schneller)',
  109.       ' ',
  110.       'Erstes Zeichen eingeben',
  111.       'Wahl ( EBHASQT<> )',
  112.       'oder ESC, um abzubrechen: '
  113.      );
  114.  
  115. {======================= zeit_vergangen =============================
  116.    Gibt die Sekunden aus, die seit dem Start der angegebenen Sortier-
  117.    routine vergangen sind.
  118.    Beachten Sie, daß diese Zeit sowohl die Zeit zum Neuzeichnen der
  119.    Balken, als auch die Pause, während der die SOUND-Anweisung eine
  120.    Note spielt, beinhaltet, und daher keinen genauen Anhaltspunkt für
  121.    die Sortiergeschwindigkeit darstellt.
  122. }
  123. PROCEDURE zeit_vergangen( akt_zeile : Integer;
  124.               akt_sort  : sort_range );
  125.  
  126.     BEGIN
  127.  
  128.     { Liest Zeit von vordefiniertem Speicher-Array. }
  129.     end_zeit := MemL[$40:$6C];
  130.  
  131.     TextColor( menu_status );
  132.     TextBackground( menu_rueck );
  133.     GotoXY( menu_links + 21, Ord(akt_sort) + menu_top + 3 );
  134.     Write( ((end_zeit - start_zeit) / ticks_pro_sek ):7:2 );
  135.  
  136.     IF ton_ein THEN
  137.     BEGIN
  138.     Sound( akt_zeile * tonleiter_frequenz );
  139.     Delay( pause );
  140.     NoSound;
  141.     END
  142.     ELSE
  143.     Delay( pause );
  144.  
  145.     TextBackground( bild_rueck );
  146.     END;  { zeit_vergangen }
  147.  
  148. {======================= swap_sort_elemente ====================
  149.     Austausch zweier Balken.
  150. }
  151. PROCEDURE swap_sort_elemente( VAR eins, zwei : sort_elemente );
  152.     VAR
  153.     temp : sort_elemente; { Austauschhilfe }
  154.     BEGIN
  155.     temp := eins;
  156.     eins := zwei;
  157.     zwei := temp;
  158.     END; { swap_sort_elemente }
  159.  
  160. {======================= Balken_zeichnen ===============================
  161.     Zeichnet einen Balken auf der vom Parameter zeile angegebenen Zeile.
  162. }
  163. PROCEDURE Balken_zeichnen( zeile : Integer );
  164.     VAR
  165.     Balken_end : Integer;
  166.     BEGIN
  167.     TextColor( sort_array[zeile].Farbe );
  168.  
  169.     Balken_end := sort_array[zeile].lng;
  170.     FillChar( Balken[1],           Balken_end,            block );
  171.     FillChar( Balken[Balken_end + 1], max_balk - Balken_end, leer );
  172.     Balken[0] := Chr( max_balk );
  173.  
  174.     GotoXY( 1, zeile );
  175.     Write( Balken );
  176.     END; { Balken_zeichnen }
  177.  
  178. {==================== swap_Balken_zeichnen =======================
  179.     Ruft Balken_zeichnen zweimal auf, um die zwei Balken in zeile1
  180.     und zeile2 zu tauschen.
  181. }
  182. PROCEDURE swap_Balken_zeichnen( zeile1, zeile2 : Integer );
  183.     BEGIN
  184.     Balken_zeichnen( zeile1 );
  185.     Balken_zeichnen( zeile2 );
  186.     END; { swap_Balken_zeichnen }
  187.  
  188. {========================= rand_int ==============================
  189.     Gibt eine Zufallszahl zurück, die größer oder gleich des
  190.     kleineren und kleiner oder gleich des größeren Parameters ist.
  191. }
  192. FUNCTION rand_int( kleiner, groesser : Integer ) : Integer;
  193.     BEGIN
  194.     rand_int := Random( groesser - kleiner ) + kleiner;
  195.     END; { rand_int }
  196.  
  197.  
  198. {$F+} { Schaltet Aufrufe für Sortierprozeduren ein. }
  199.  
  200. { ========================= Bubble_sort ===============================
  201.     Der "BubbleSort"-Algorithmus durchläuft sort_array, vergleicht auf-
  202.     einanderfolgende Elemente und vertauscht Paare, die nicht in der
  203.     richtigen Reihenfolge vorliegen. Er fährt damit fort, bis keine
  204.     Paare mehr getauscht wurden.
  205. }
  206. PROCEDURE Bubble_sort;
  207.     VAR zeile,              { Element zeile wird mit zeile + 1 verglichen }
  208.     tausch,             { Zeile, wo Elemente getauscht werden }
  209.     grenze   : Integer; { Letztes zu vergleichendes Element - 1 }
  210.  
  211.     BEGIN
  212.     grenze := max_balk;
  213.     REPEAT
  214.     tausch := 0;
  215.     FOR zeile := 1 TO grenze - 1 DO
  216.         { Zwei aufeinanderfolgende Elemente liegen nicht in der richtigen
  217.           Reihenfolge vor, also tausche deren Werte und zeichne ihre
  218.           Balken neu:
  219.         }
  220.         IF (sort_array[zeile].lng > sort_array[zeile + 1].lng) THEN
  221.         BEGIN
  222.         swap_sort_elemente( sort_array[zeile], sort_array[zeile + 1] );
  223.         swap_Balken_zeichnen( zeile, zeile + 1 );
  224.         zeit_vergangen( zeile, Bubble );
  225.         tausch := zeile;
  226.         END;
  227.  
  228.     { Sortiere im nächsten Schritt nur bis dahin, wo der letzte Tausch
  229.       vorgenommen wurde:
  230.     }
  231.     grenze := tausch;
  232.  
  233.     UNTIL (tausch = 0);
  234.     END; { Bubble_sort }
  235.  
  236.  
  237. {======================= Austausch_sort ==========================
  238.     Der Algorithmus "Sortieren durch Austauschen" vergleicht jedes
  239.     Element in sort_array - beginnend mit dem ersten Element - mit
  240.     jedem folgenden Element. Wenn eines der nachfolgenden Elemente
  241.     kleiner ist als das aktuelle Element, wird es mit dem aktuellen
  242.     Element getauscht, und der Ablauf wird mit dem nächsten Element
  243.     in sort_array wiederholt.
  244. }
  245. PROCEDURE Austausch_sort;
  246.     VAR zeile,                  { Zeile, die verglichen wird }
  247.     kl_zeile,               { Kleinste, gefundene Zeile }
  248.     j      : Integer;
  249.     BEGIN
  250.     FOR zeile := 1 TO max_balk - 1 DO
  251.     BEGIN
  252.     kl_zeile := zeile;
  253.     FOR j := zeile + 1 TO max_balk DO
  254.         BEGIN
  255.         IF (sort_array[j].lng < sort_array[kl_zeile].lng) THEN
  256.         BEGIN
  257.         kl_zeile := j;
  258.         zeit_vergangen( j, Austausch );
  259.         END;
  260.         END;
  261.     IF (kl_zeile > zeile) THEN
  262.         { Zeile gefunden, die kleiner als die aktuelle Zeile
  263.           ist, also vertausche diese beiden Datenfeldelemente:
  264.         }
  265.         BEGIN
  266.         swap_sort_elemente( sort_array[zeile], sort_array[kl_zeile] );
  267.         swap_Balken_zeichnen( zeile, kl_zeile );
  268.         zeit_vergangen( zeile, Austausch );
  269.         END;
  270.     END;
  271.     END;  { Austausch_sort }
  272.  
  273.  
  274. {============================== Heap_sort ==============================
  275.     Die Prozedur HeapSort funktioniert, indem sie zwei andere Prozeduren
  276.     aufruft - filtern_aufw und filtern_abw. filtern_aufw wandelt
  277.     sort_array in einen "Heap" um, dessen Eigenschaften das unten
  278.     gezeigte Diagramm verdeutlicht:
  279.  
  280.                 sort_array(1)
  281.                /            \
  282.           sort_array(2)                sort_array(3)
  283.            /       \                   /       \
  284.     sort_array(4)    sort_array(5)   sort_array(6)  sort_array(7)
  285.        /  \            /  \           /  \          /  \
  286.      ...  ...        ...  ...       ...  ...      ...  ...
  287.  
  288.     wobei jeder "Eltern-Knoten" größer ist als jeder seiner "Kind-Knoten";
  289.     zum Beispiel ist sort_array(1) größer als sort_array(2) oder
  290.     sort_array(3), sort_array(3) ist größer als sort_array(6) oder
  291.     sort_array(7) und so weiter.
  292.  
  293.     Nachdem die erste for-Schleife in Heap_sort beendet ist, befindet
  294.     sich das größte Element daher in sort_array(1).
  295.  
  296.     Die zweite for-Schleife in Heap_sort vertauscht das Element in
  297.     sort_array(1) mit dem Element in max_ebene, bildet den Heap erneut
  298.     (mit filtern_abw) für max_ebene - 1, vertauscht anschließend das
  299.     Element in sort_array(1) mit dem Element in max_ebene - 1, bildet
  300.     den Heap erneut für max_ebene - 2 und fährt in dieser Art und Weise
  301.     fort, bis das Array sortiert ist.
  302.  }
  303. PROCEDURE Heap_sort;
  304.  
  305.     {=================== filtern_abw =================================
  306.     Die Prozedur FilternAbw erzeugt mit den Elementen aus sort_array
  307.     von 1 bis max_ebene erneut einen "Heap" (siehe das Diagramm in der
  308.     Prozedur HeapSort).
  309.     }
  310.     PROCEDURE filtern_abw( max_ebene : Integer );
  311.     VAR
  312.         i,
  313.         kind   : Integer;  { Kind des zu vergleichenden Elements }
  314.         fertig : Boolean;  { Wahr, wenn beendet }
  315.     BEGIN
  316.     i := 1;
  317.     fertig := False;
  318.  
  319.     { Bewege den Wert in sort_array(1) im Heap solange nach unten, bis
  320.       dieser seinen richtigen Knoten erreicht hat (das heißt, bis der
  321.       Wertkleiner als sein Eltern-Knoten ist, oder bis er max_ebene,
  322.       die unterste Ebene des aktuellen Heaps, erreicht hat):
  323.     }
  324.     WHILE (NOT fertig) DO
  325.         BEGIN
  326.         { Index des Kind-Knoten ermitteln. }
  327.         kind := 2 * i;
  328.         IF (kind > max_ebene) THEN
  329.         fertig := True  { Unterste Ebene des Heaps erreicht,
  330.                   also Prozedur verlassen. }
  331.         ELSE
  332.         BEGIN
  333.         { Bei 2 Kind-Knoten den Größeren ermitteln. }
  334.         IF (kind + 1 <= max_ebene) THEN
  335.             IF (sort_array[kind + 1].lng >
  336.             sort_array[kind    ].lng) THEN
  337.             kind := kind + 1;
  338.         { Bewege den Wert nach unten, solange er noch nicht
  339.           größer als irgendeines seiner Kinder ist:
  340.         }
  341.         IF (sort_array[i].lng < sort_array[kind].lng) THEN
  342.             BEGIN
  343.             swap_sort_elemente( sort_array[i], sort_array[kind] );
  344.             swap_Balken_zeichnen( i, kind );
  345.             zeit_vergangen( i, Heap );
  346.             i := kind;
  347.             END
  348.         ELSE
  349.             { Andernfalls ist sort_array erneut als Heap von 1 bis
  350.               max_ebene aufgebaut, also beenden:
  351.             }
  352.             fertig := True;
  353.         END;
  354.         END;
  355.     END;  { filtern_abw }
  356.  
  357.     {======================= filtern_aufw ==============================
  358.     Die Prozedur filtern_aufw überträgt die Elemente von 1 bis max_ebene
  359.     in sort_array in einen "Heap" (siehe das Diagramm in der Prozedur
  360.     Heap_sort).
  361.     }
  362.     PROCEDURE filtern_aufw( max_ebene : Integer );
  363.     VAR
  364.         i,
  365.         eltern : Integer;  { Eltern des zu vergleichenden Elements }
  366.     BEGIN
  367.     i := max_ebene;
  368.  
  369.     { Bewege den Wert in sort_array(max_ebene) solange durch den Heap
  370.       nach oben, bis er seinen richtigen Knoten erreicht hat (das heißt,
  371.       bis der Wert größer als irgendeiner seiner Kind-Knoten ist, oder
  372.       er 1, die Spitze des Heaps, erreicht hat):
  373.     }
  374.     WHILE (i <> 1) DO
  375.         BEGIN
  376.         eltern := i DIV 2;  { Index des Eltern-Knoten lesen }
  377.         IF (sort_array[i].lng > sort_array[eltern].lng) THEN
  378.         { Der Wert des aktuellen Knotens ist noch größer als der
  379.           Wert seines Eltern-Knotens, also vertausche diese beiden
  380.           Array-Elemente:
  381.         }
  382.         BEGIN
  383.         swap_sort_elemente( sort_array[eltern], sort_array[i] );
  384.         swap_Balken_zeichnen( eltern, i );
  385.         zeit_vergangen( eltern, Heap );
  386.         i := eltern;
  387.         END
  388.         ELSE
  389.         { Andernfalls hat das Element in dem Heap seine richtige
  390.           Position erreicht, also verlasse diese Prozedur:
  391.         }
  392.         i := 1;
  393.         END;
  394.     END; { filtern_aufw }
  395.  
  396.     { ====================================================
  397.       Deklarationen und Code für Heap_sort
  398.     }
  399.     VAR
  400.     i : Integer;
  401.     BEGIN
  402.     FOR i := 2 TO max_balk DO filtern_aufw( i );
  403.     FOR i := max_balk DOWNTO 2 DO
  404.     BEGIN
  405.     swap_sort_elemente( sort_array[1], sort_array[i] );
  406.     swap_Balken_zeichnen( 1, i );
  407.     zeit_vergangen( 1, Heap );
  408.     filtern_abw( i - 1 );
  409.     END;
  410.     END;  { Heap_sort }
  411.  
  412. {============================ Einfuegen_sort ===========================
  413.     Die Prozedur Einfuegen_sort vergleicht nacheinander die Länge jedes
  414.     Elementes in sort_array mit der Länge aller vorhergehenden Elemente.
  415.     Nachdem die Prozedur die entsprechende Position für das neue Element
  416.     gefunden hat, fügt es das Element an seinem neuen Platz ein und
  417.     bewegt alle anderen Elemente um eine Position nach unten.
  418. }
  419. PROCEDURE Einfuegen_sort;
  420.     VAR
  421.     j,
  422.     zeile,                  { Einzufügende Zeile }
  423.     temp_Laenge : Integer;  { Länge der aktuellen Zeile }
  424.     temp : sort_elemente;   { Aktueller Zeilenwert }
  425.     BEGIN
  426.     FOR zeile := 2 TO max_balk DO
  427.     BEGIN
  428.     temp := sort_array[zeile];
  429.     temp_Laenge := temp.lng;
  430.     j := zeile;
  431.     WHILE ((j >= 2) AND (sort_array[j - 1].lng > temp_Laenge)) DO
  432.         BEGIN
  433.         sort_array[j] := sort_array[j - 1];
  434.         Balken_zeichnen( j );            { Neuen Balken zeichnen. }
  435.         zeit_vergangen( j, Einfuegen );  { Verstrichene Zeit ausgeben. }
  436.         Dec( j );
  437.         END;
  438.  
  439.     { Ursprünglichen Wert von sort_array(zeile)
  440.       in sort_array(j) einfügen. }
  441.     sort_array[j] := temp;
  442.     Balken_zeichnen( j );                { Neuen Balken zeichnen. }
  443.     zeit_vergangen( j, Einfuegen );      { Verstrichene Zeit ausgeben. }
  444.     END;
  445.     END;  { Einfuegen_sort }
  446.  
  447. { ========================= Quick_sort ================================
  448.   Der "QuickSort"-Algorithmus funktioniert, indem er ein zufälliges
  449.   "Pivot"- Element aus sort_array herausnimmt, anschließend jedes
  450.   Element, das größer ist, auf eine Seite des Pivot-Elementes bewegt,
  451.   und jedes Element, das kleiner ist, auf die andere Seite bewegt.
  452.   QuickSort wird dann mit den beiden Unterabteilungen, die von dem
  453.   Pivot-Element erzeugt wurden, rekursiv aufgerufen. Nachdem die Anzahl
  454.   der Elemente in einer Untermenge einmal zwei erreicht hat, enden
  455.   die rekursiven Aufrufe, und das Datenfeld ist sortiert.
  456. }
  457. PROCEDURE Quick_sort;
  458.  
  459.  
  460.     PROCEDURE qsort( klein, gross : Integer );
  461.     VAR
  462.         i, j, pivot : Integer;
  463.     BEGIN
  464.     IF (klein < gross) THEN
  465.         BEGIN
  466.         { Nur zwei Elemente in dieser Unterabteilung; vertausche diese,
  467.           wenn sie nicht in der richtigen Reihenfolge vorliegen und
  468.           beende anschließend die rekursiven Aufrufe:
  469.         }
  470.         IF (gross - klein = 1) THEN
  471.         BEGIN
  472.         IF (sort_array[klein].lng > sort_array[gross].lng) THEN
  473.             BEGIN
  474.             swap_sort_elemente( sort_array[klein], sort_array[gross] );
  475.             swap_Balken_zeichnen( klein, gross );
  476.             zeit_vergangen( klein, Quick );
  477.             END;
  478.         END
  479.         ELSE
  480.         BEGIN
  481.         pivot := sort_array[gross].lng;
  482.         i := klein;
  483.         j := gross;
  484.         WHILE (i < j)   DO
  485.             BEGIN
  486.             { Hinbewegung von beiden Seiten auf das Pivot-Element zu. }
  487.             WHILE ((i < j) AND (sort_array[i].lng <= pivot)) DO
  488.             Inc( i );
  489.             WHILE ((j > i) AND (sort_array[j].lng >= pivot)) DO
  490.             Dec( j );
  491.             { Wird das Pivot-Element nicht erreicht, bedeutet dies,
  492.               daß zwei Elemente auf einer Seite nicht in der rich-
  493.               tigen Reihenfolge vorliegen, also vertausche diese
  494.               Elemente.
  495.             }
  496.             IF (i < j) THEN
  497.             BEGIN
  498.             swap_sort_elemente( sort_array[i], sort_array[j] );
  499.             swap_Balken_zeichnen( i, j );
  500.             zeit_vergangen( i, Quick );
  501.             END;
  502.             END;
  503.  
  504.         { Bewege das Pivot-Element zurück auf seinen richtigen
  505.           Platz im Array.
  506.         }
  507.         swap_sort_elemente( sort_array[i], sort_array[gross] );
  508.         swap_Balken_zeichnen( i, gross );
  509.         zeit_vergangen( i, Quick );
  510.  
  511.         { Rufe die Prozedur Quick_sort rekursiv auf (übergib die
  512.           kleinere Unterabteilung zuerst, um weniger Stapelplatz
  513.           zu verwenden).
  514.         }
  515.         IF ((i - klein) < (gross - i)) THEN
  516.             BEGIN
  517.             qsort( klein, i - 1 );
  518.             qsort( i + 1, gross );
  519.             END
  520.         ELSE
  521.             BEGIN
  522.             qsort( i + 1, gross );
  523.             qsort( klein, i - 1 );
  524.             END;
  525.         END;
  526.         END;
  527.     END;  { qsort }
  528.  
  529.     { =========================================================
  530.       Code für Quick_sort
  531.     }
  532.     BEGIN
  533.     qsort( 1, max_balk );
  534.     END;
  535.  
  536.  
  537. {============================= Shell_sort =============================
  538.   Die Prozedur Shell_sort ist ähnlich zu der Prozedur Bubble_Sort.
  539.   Shell_sort startet jedoch damit, daß sie weit auseinanderliegende
  540.   Elemente vergleicht (getrennt durch den Wert der Variablen offset,
  541.   der zu Beginn die Hälfte des Abstandes zwischen dem ersten und
  542.   letzten Element ist) und anschließend Elemente vergleicht, die näher
  543.   zusammenliegen (wenn offset eins ist, ist die letzte Iteration dieser
  544.   Prozedur gleich der Prozedur Bubble_Sort).
  545. }
  546. PROCEDURE Shell_sort;
  547.     VAR
  548.     offset,            { Vergleichsoffset }
  549.     tausch,            { Zeile, wo letzter Tausch auftrat }
  550.     grenze,            { Anzahl der Elemente, die jedesmal verglichen
  551.                          werden }
  552.     zeile   : Integer; { Aktuelle Zeile }
  553.     BEGIN
  554.     { Setze den Vergleichsoffset auf die Hälfte der Satzzahl in
  555.       sort_array }
  556.     offset := max_balk DIV 2;
  557.  
  558.     WHILE (offset > 0) DO
  559.     BEGIN
  560.     { Schleife, bis offset Null wird. }
  561.     grenze := max_balk - offset;
  562.     REPEAT
  563.         tausch := 0; { Kein Tausch bei diesem Offset }
  564.         { Elemente vergleichen und diejenigen vertauschen,
  565.           die nicht in der Reihenfolge liegen. }
  566.         FOR zeile := 1 TO grenze DO
  567.         IF (sort_array[zeile].lng >
  568.             sort_array[zeile + offset].lng) THEN
  569.             BEGIN
  570.             swap_sort_elemente( sort_array[zeile],
  571.                     sort_array[zeile + offset] );
  572.             swap_Balken_zeichnen( zeile, zeile + offset );
  573.             zeit_vergangen( zeile, Shell );
  574.             tausch := zeile;
  575.             END;
  576.  
  577.         { Sortiere im nächsten Schritt nur bis dahin, wo der letzte
  578.           Tausch durchgeführt wurde.
  579.         }
  580.         grenze := tausch - offset;
  581.     UNTIL (tausch = 0);
  582.     { Kein Tausch beim letzten Offset, versuche es mit dem
  583.       halbierten Offset.
  584.     }
  585.     offset := offset DIV 2;
  586.     END;
  587.     END;  { Shell_sort }
  588. {$F-} { Schaltet FAR-Aufrufe aus. }
  589.  
  590. {======================= Monitor =========================
  591.  Setzt die Ausgabe auf die höchste Anzahl von verfügbaren
  592.  Textzeilen und die Anzahl der Farben
  593. }
  594. PROCEDURE Monitor;
  595.     BEGIN
  596.     IF LastMode = Mono THEN
  597.     BEGIN
  598.     max_Farben     := 1;
  599.     TextMode( Mono );
  600.     bild_rueck     := Black;
  601.     menu_rueck     := Black;
  602.     menu_rahmen    := LightGray;
  603.     menu_text      := LightGray;
  604.     menu_status    := LightGray;
  605.     menu_hervorghb := White;
  606.     END
  607.     ELSE
  608.     BEGIN
  609.     max_Farben := 15;
  610.     TextMode( CO80 + Font8x8 );
  611.     END;
  612.     max_balk := Hi( WindMax );  { Ermittelt Anzahl der Textzeilen. }
  613.     IF max_balk > maxmax_balk THEN
  614.     max_balk := maxmax_balk;
  615.     END;  { Monitor }
  616.  
  617. {========================= zeichne_menu =============================
  618.     Ruft die Prozedur zeichne_rahmen auf, um einen Rahmen um das Menü
  619.     zu zeichnen, danach wird die "Menüauswahl" ausgegeben.
  620. }
  621. PROCEDURE zeichne_menu;
  622.  
  623.     {======================= zeichne_rahmen =========================
  624.     Zeichnet mit den ASCII-Zeichen höherer Ordnung ╔ (201), ╗ (187),
  625.     ╚ (200), ╝ (188) , ║ (186) und ═ (205) einen rechteckigen Rahmen.
  626.     Die Parameter SeiteOben, SeiteUnten, SeiteLinks und SeiteRechts
  627.     sind die Zeilen- bzw. Spaltenargumente der oberen linken bzw.
  628.     unteren rechten Ecke des Rahmens.
  629.     }
  630.     PROCEDURE zeichne_rahmen( top, left, menu_breite, height : Integer );
  631.     CONST
  632.         olinks     = #201;  { Größeres linkes Zeichen }
  633.         orechts    = #187;  { Größeres rechtes Zeichen }
  634.         ulinks     = #200;  { Kleineres linkes Zeichen }
  635.         urechts    = #188;  { Kleineres rechtes Zeichen }
  636.         vertikal   = #186;  { Vertikales Linienzeichen }
  637.         horizontal = #205;  { Horizontales Linienzeichen }
  638.     VAR
  639.         linie : CSTRING[80]; { Horizontales Stück der Box }
  640.         i     : Integer;
  641.     BEGIN
  642.  
  643.     FillChar( linie[2], menu_breite - 2, horizontal );
  644.     linie[menu_breite + 1] := null_zchn;
  645.  
  646.     linie[1] := olinks;
  647.     linie[menu_breite] := orechts;
  648.     GotoXY( left, top );
  649.     Write( linie );
  650.  
  651.     linie[1] := ulinks;
  652.     linie[menu_breite] := urechts;
  653.     GotoXY( left, top + height );
  654.     Write( linie );
  655.  
  656.     FillChar( linie[2], menu_breite - 2, leer );
  657.     linie[1] := vertikal;
  658.     linie[menu_breite] := vertikal;
  659.  
  660.     FOR i := top + 1 TO top + height - 1 DO
  661.         BEGIN
  662.         GotoXY( left, i );
  663.         Write( linie );
  664.         END;
  665.     END;  { zeichne_rahmen }
  666.  
  667.     { ==========================================================
  668.       Deklarationen und Code für zeichne_menu
  669.     }
  670.     CONST
  671.     ein_aus : ARRAY[Boolean] OF STRING[3] = ('AUS', 'EIN');
  672.     VAR
  673.     i : Integer;
  674.     BEGIN
  675.     TextBackground( menu_rueck );
  676.     TextColor( menu_rahmen );
  677.     zeichne_rahmen( 1, menu_links - 3, menu_breite + 3, 20 );
  678.  
  679.     TextColor( menu_text );
  680.     FOR i := 1 TO menu_hoehe DO
  681.     BEGIN
  682.     GotoXY( menu_links, menu_top + i );
  683.     Write( menu[i] );
  684.  
  685.     IF (i IN [3..10, 13, 14]) THEN
  686.         BEGIN
  687.         TextColor( menu_hervorghb );
  688.         GotoXY( menu_links, menu_top + i );
  689.         Write( menu[i][1] );
  690.         TextColor( menu_text );
  691.         END;
  692.  
  693.     END; { FOR }
  694.  
  695.     TextColor( menu_status );
  696.     { Aktuelle ton_ein-Werte ausgeben. }
  697.     GotoXY( menu_links + 14, 11 );
  698.     Write( ein_aus[ton_ein] );
  699.  
  700.     { Pause-Wert ausgeben. }
  701.     GotoXY( menu_links + 13, 13 );
  702.     Write( (pause DIV 20):3 );
  703.  
  704.     { Option für Geschwindigkeit löschen, falls die Pausenlänge
  705.       ihre Grenze erreicht. }
  706.     IF (pause = 900) THEN
  707.     BEGIN
  708.     GotoXY( menu_links, 14 );
  709.     Write( '':12 );
  710.     END
  711.     ELSE IF (pause = 0) THEN
  712.     BEGIN
  713.     GotoXY( menu_links, 15 );
  714.     Write( '':12 );
  715.     END;
  716.     TextBackground( bild_rueck );
  717.     END;  { zeichne_menu }
  718.  
  719. {======================= initialisieren ============================
  720.      unsort_array initialisieren.
  721. }
  722. PROCEDURE initialisieren;
  723.     VAR
  724.     i,
  725.     index,
  726.     max_index,                { Maximaler Index für Initialisierung }
  727.     Balken_Laenge : Integer;  { Länge des initialisierten Balkens }
  728.     temp : ARRAY[1..maxmax_balk] OF Integer;  { Balken-Array }
  729.     BEGIN
  730.  
  731.     Randomize;
  732.     FOR i := 1 TO max_balk DO temp[i] := i;
  733.     max_index := max_balk;
  734.     FOR i := 1 TO max_balk DO
  735.     BEGIN
  736.     { Zufallselement in temp zwischen 1 und max_index finden,
  737.       dann Wert des Elementes Balken_Laenge zuweisen.
  738.     }
  739.     index := rand_int( 1, max_index );
  740.     Balken_Laenge := temp[index];
  741.     { Den Wert in temp[index] mit dem Wert in temp[max_index]
  742.       überschreiben, so daß der Wert in temp[index] nur einmal
  743.       gewählt wird.
  744.     }
  745.     temp[index] := temp[max_index];
  746.     { Den Wert von max_index verkleinern, so daß temp[max_index]
  747.       beim nächsten Schritt durch die Schleife nicht gewählt
  748.       werden kann.
  749.     }
  750.     Dec( max_index );
  751.  
  752.     unsort_array[i].lng := Balken_Laenge;
  753.     IF (max_Farben = 1) THEN
  754.         unsort_array[i].Farbe := LightGray
  755.     ELSE
  756.         unsort_array[i].Farbe := (Balken_Laenge MOD max_Farben) + 1;
  757.     END;
  758.  
  759.     END;  { initialisieren }
  760.  
  761.  
  762. {======================= reinitialisieren ===========================
  763.     Überführt sort_array wieder in seinen ursprünglichen unsortierten
  764.     Zustand und gibt anschließend die unsortierten Farbbalken aus.
  765. }
  766. PROCEDURE reinitialisieren;
  767.     VAR
  768.     zeile      : Integer;  { Indizierung des Balken-Array }
  769.     BEGIN
  770.  
  771.     FOR zeile := 1 TO max_balk DO
  772.     BEGIN
  773.     sort_array[zeile] := unsort_array[zeile];
  774.     Balken_zeichnen( zeile );
  775.     END;
  776.  
  777.     { Startzeit von vordefiniertem Speicher-Array einlesen. }
  778.     start_zeit := MemL[$40:$6C];
  779.     END; { Reinitialisieren }
  780.  
  781.  
  782. {======================= sort_menu ===========================
  783.     Fordert den Benutzer auf:
  784.     - Einen der Sortier-Algorithmen zu wählen
  785.     - Ton ein- bzw. auszuschalten
  786.     - Geschwindigkeit zu erhöhen bzw. vermindern
  787.     - Das Programm zu beenden
  788. }
  789. PROCEDURE sort_menu;
  790.  
  791.     {======================= sortieren ========================
  792.      Array initialisieren und gewählten Algorithmus auszuführen,
  793.      ausgeführte Zeit ausgeben.
  794.     }
  795.     PROCEDURE sortieren( sort_art : sort_range );
  796.  
  797.     {======================= cursor_schalten =======================
  798.      Sichtbarkeit des Cursors wechseln.
  799.      Ergibt Wahr, falls der Cursor nach dem Umschalten sichtbar ist.
  800.      Annahme der Videoseite 0.
  801.     }
  802.     FUNCTION cursor_schalten : Boolean;
  803.         VAR
  804.         r : Registers;
  805.         BEGIN
  806.         r.AH := 3;  { Cursorfunktion einlesen }
  807.         r.BH := 0;  { Annahme der Videoseite 0 }
  808.         Intr( 16, r );
  809.         r.AH := 1;
  810.         r.CH := r.CH XOR $20; { Sichtbares Bit umschalten }
  811.         cursor_schalten := ( r.CH AND $20 ) = 0;
  812.         Intr( 16, r );
  813.         END; { cursor_schalten }
  814.  
  815.     { ==================================================
  816.       Deklarationen und Code für sortieren
  817.     }
  818.     VAR
  819.         b : Boolean;    {  Dummy für cursor_schalten }
  820.     BEGIN
  821.     reinitialisieren;   { Array reinitialieren (zum unsort. Status). }
  822.     IF cursor_schalten THEN       { Überprüfen, ob Schalter aus ist. }
  823.         b := cursor_schalten ;
  824.     sort[sort_art];               { Sortieren. }
  825.     zeit_vergangen( 0, sort_art );{ Ausführungszeit ausgeben. }
  826.     b := cursor_schalten;         { Cursor einschalten. }
  827.     END; { sortieren }
  828.  
  829.     { =======================================================
  830.        Deklarationen und Code für sort_menu
  831.     }
  832.     VAR
  833.     zch   : Char;     { Zeichen von Tastatur einlesen }
  834.     fertig : Boolean; { Wahr, falls ESC gedrückt wurde }
  835.     BEGIN
  836.     fertig := False;
  837.     WHILE NOT fertig DO
  838.     BEGIN
  839.     GotoXY( menu_links + Length( menu[menu_hoehe] ),
  840.         menu_top + menu_hoehe );
  841.     zch := UpCase( ReadKey );
  842.     CASE zch OF
  843.         'E' : sortieren( Einfuegen );
  844.         'B' : sortieren( Bubble );
  845.         'H' : sortieren( Heap );
  846.         'A' : sortieren( Austausch );
  847.         'S' : sortieren( Shell );
  848.         'Q' : sortieren( Quick );
  849.         '>',
  850.         '.' :  { Pause verringeren. }
  851.         IF (pause > 0) THEN
  852.             BEGIN
  853.             pause := pause - 20;
  854.             zeichne_menu;  { Alte Zeiten löschen - nicht mehr gültig. }
  855.             END;
  856.         '<',
  857.         ',' :  { Pause erhöhen. }
  858.         IF (pause < 900) THEN
  859.             BEGIN
  860.             pause := pause + 20;
  861.             zeichne_menu;  { Alte Zeiten löschen - nicht mehr gültig. }
  862.             END;
  863.         'T' :   { Ton umschalten. }
  864.         BEGIN
  865.         ton_ein := NOT ton_ein;
  866.         zeichne_menu;
  867.         END;
  868.         esc : fertig := True;
  869.         #0  : zch := ReadKey;
  870.         ELSE  { Jede andere Eingabe ignorieren. }
  871.         END;
  872.     END;
  873.     END;  { sort_menu }
  874.  
  875.  
  876. {===================== Hauptprogramm ============================}
  877. BEGIN
  878.  
  879.     { Array der Sortier-Prozeduren initialisieren. }
  880.     sort[Einfuegen] := Einfuegen_sort;
  881.     sort[Bubble]    := Bubble_sort;
  882.     sort[Heap]      := Heap_sort;
  883.     sort[Austausch] := Austausch_sort;
  884.     sort[Shell]     := Shell_sort;
  885.     sort[Quick]     := Quick_sort;
  886.  
  887.     start_modus     := LastMode;  { Start-Videomodus speichern. }
  888.     Monitor; { Monitor einrichten, max_balk und max_Farben ermitteln. }
  889.  
  890.     tonleiter_frequenz := 5000 DIV max_balk; { Tonleiter-Faktor }
  891.     ton_ein         := True;                 { Ton ist nach Vorgabe ein }
  892.     pause           := 60;                   { Vorgabewert für Pause }
  893.  
  894.     initialisieren;   { Datenwerte initialisieren }
  895.     { Werte in unsort_array auf sort_array zuweisen
  896.       und unsortierte Balken ausgeben.
  897.     }
  898.     reinitialisieren;
  899.     zeichne_menu;
  900.     sort_menu;    { Menü aktivieren }
  901.  
  902.     TextMode( start_modus );  { Videomodus wiederherstellen }
  903.  
  904. END.
  905.