Informaticasite van het Lauwers College te Buitenpost                 © R.J. van der Beek
 

Hoofdstuk 6 Programmeren in Pascal

6.5 Sorteren.

  6.5.1. Sorteren

Bekijk de volgende lijst met namen



Je ziet natuurlijk dat de namen niet in alfabetische volgorde staan, ze zijn niet gesorteerd.
Als je ze wel gaat sorteren dan komt de lijst er als volgt uit te zien:



Er zijn verschillende manieren om de computer zo'n lijst met woorden of getallen te laten sorteren.
Dat worden sorteeralgoritmen genoemd.
Er wordt ontzettend veel computertijd gebruikt voor het sorteren van grote hoeveelheden gegevens, en daarom is het heel belangrijk om over goede sorteer-methoden te beschikken. In de afgelopen vijftig jaren zijn er veel sorteer-algoritmen ontwikkeld.
Wij gaan twee vrij eenvoudige methoden bekijken: het bubblesort-algoritme en het selectionsort-algoritme.

  6.5.2. Het bubblesort-algoritme

De bubblesort-methode houdt in dat je steeds twee opeenvolgende woorden of getallen vergelijkt. Als die woorden of getallen in de juiste volgorde staan wordt er niets gedaan, maar anders worden ze omgewisseld. En daarmee ga je net zo lang door tot ze allemaal op de goede plaats staan.

Hier onder zie je een applet (gemaakt door Robert Lafore), waarin dat gedemonstreerd wordt.


New Genereert nieuwe data en initialiseert. De optie 'new' wisselt tussen random en aflopend.

Size Wisselt tussen 10 en 100 balkjes. Genereert ook nieuwe data en initialiseert.

Draw Tekent de balkjes opnieuw.

Run Start het automatisch sorteren (klik op 'Step' om te pauzeren en 'Run' om weer verder te gaan).

Step Voert één stap van het sorteerproces uit.

In het volgende voorbeeld demonstreren we het met de namen uit paragraaf 6.5.1.
Daarbij begin je met het laatste en één-na-laatste woord of getal, en daarna vergelijk je het één-na-laatste en het twee-na-laatste woord of getal, en zo ga je steeds verder naar voren.



Als voorbeeld gaan we bij bovenstaande rij de woorden van achteren naar voren doorlopen, en zonodig steeds twee buur-woorden verwisselen.

Het vierde en vijfde woord staan in de juiste volgorde, die laten we dus staan.
Het derde en vierde woord staan in de verkeerde volgorde, die wisselen we dus om.




Het resultaat van deze Verwissel(3, 4)-bewerking zie je in de bovenste rij hier onder.
En ook hoe het verder gaat als je verder naar voren loopt.
Als je het eerste en tweede woord vergeleken hebt, en die eventueel verwisseld hebt, begin je weer bij de laatste twee. Zo ga je steeds verder.



Bij deze aanpak wordt bij elke doorgang van achteren naar voren de kleinste waarde steeds naar voren geschoven. Na de eerste doorgang ben je er zeker van dat het eerste woord ook werkelijk vooraan staat.
Na de tweede doorgang ben je er zeker van dat het tweede woord ook werkelijk op de tweede plaats staat.
Als je de linkerkant als boven ziet (je moet de figuur eigenlijk een kwart slag rechtsom draaien), dan borrelen de lichtste waarden als het ware als luchtbelletjes naar boven, vandaar de naam bubblesort.
Je kunt zien, dat vanaf de linkerkant het gesorteerde deel van de rij toeneemt; dat gedeelte van de rij is met een kleurtje aangegeven.
Als er bij een bepaalde doorgang geen enkele verwisseling plaats vind is de rij gesorteerd, en dan is het klaar.

We zetten nu eerst in een structuurdiagram wat er in grote lijnen moet gebeuren:



Nu gaan we het eerste deelprobleem (namen invoeren) verfijnen.
We weten niet hoeveel namen er zullen worden ingevoerd, we gaan ervan uit dat er telkens een naam wordt ingevoerd. En als er op een gegeven moment, in plaats van het invoeren van een naam, direkt op enter wordt gedrukt (de naam is dan dus de lege string) nemen we aan dat er gestopt wordt met het invoeren van namen.
Dan moet dus het volgende gebeuren, in een structuurdiagram genoteerd:



Het tweede deelprobleem (sorteren) bestaat uit meer stappen als we het gaan verfijnen.
We gaan niet direkt tot in de finesses verfijnen, we doen het weer in etappes.
Eerst verfijnen we het als volgt:



Het deelprobleem: Maak een doorloop enz..... gaan we nu verder verfijnen.
In een structuurdiagram wordt dat het volgende:



Het deelprobleem: Verwissel (teller-1, teller) moeten we ook nog in stapjes verdelen.
Als je de inhoud van twee variabelen a en b wilt verwisselen, dan kan dat niet met de opdrachten:
a := b;
b := a;
Want stel je voor dat a het woord mies bevat, en b het woord aap.
Dan zal na de opdracht a := b de variabele a het woord aap bevatten, en b bevat ook nog steeds het woord aap.
En dan ben je het woord mies kwijt!
En na de volgende opdracht b := a zal de variabele b het woord aap bevatten, en a bevat ook het woord aap.
Dan zijn ze dus niet verwisseld.
Je hebt een extra hulpvariabele nodig, waarin het eerste woord wordt bewaard.
Het moet als volgt:

hulp := b;
b := a;
a := hulp;

Het laatste deelprobleem (de gesorteerde lijst) kun je vast zelf wel verfijnen.

Nu gaan we het struktuurdiagram omzetten in een programma.
Van elke deelprobleem, dat we hebben verfijnd in een nieuw struktuurdiagram, maken we een procedure.
We voegen de header toe, en de declaraties. En dan krijgen we het volgende programma:

program bubblesort;

var aantal: integer;
   namen: array [1..100] of string;

procedure invoeren;
var naam: string;
   i: integer;
begin
   {de teller krijgt eerst de waarde 1}
   i:=1;
   {de variabele naam moet een (willekeurige) waarde krijgen
   want als naam gelijk is aan de lege string zal de  
   while-lus direkt stoppen}
   naam:="test";
   while naam<>"" do
   begin
      write("Geef naam nummer ",i:2,"        ");
      readln(naam);
      namen[i]:=naam;
      {voor het vervolg de letter met 1 ophogen}
      i:=i+1;
   end;
   {als er gestopt wordt is het aantal namen gelijk aan i-1}
   aantal:=i-1;
end;

procedure verwissel(var a:string; var b:string);
var hulp: string;
begin
   hulp:=a;
   a:=b;
   b:=hulp;
end;

procedure maakdoorloop(var ietsverwisseld:boolean);
var i: integer;
begin
   ietsverwisseld:=false;
   for i:=aantal downto 2 do
   begin
      if (namen[i-1]>namen[i] ) then
      begin
         verwissel(namen[i-1],namen[i]);
         ietsverwisseld:=true;
      end;
   end;
end;

procedure sorteer;
var ietsverwisseld: boolean;
begin
	repeat
	   maakdoorloop(ietsverwisseld)
	until (ietsverwisseld=false);
end;

procedure drukaf;
var i:integer;
begin
   writeln;writeln;writeln("de gesorteerde lijst:");
   for i:=1 to aantal do
   writeln(namen[i]);
end;


begin
   invoeren;
   sorteer;
   drukaf;
end.

en als we het programma dan uitvoeren kan het volgende verschijnen:

  6.5.3. Het selectionsort-algoritme

Bij de selectionsort-methode zoek je eerst het kleinste element van de rij met woorden (of getallen) op. En dat kleinste element verwissel je dan met het eerste woord of getal van de rij.
Vervolgens zoek je het kleinste element vanaf het tweede woord (of getal) op. En dat kleinste element verwissel je dan met het tweede woord of getal van de rij.
En zo ga je steeds verder.
Steeds zoek je het kleinste element op van het nog ongesorteerde deel van de rij, en dat kleinste element verwissel je dan met het éérste element van het nog ongesorteerde deel van de rij.
Op die manier vergroot je het gesorteerde deel steeds met één element.

Je bent klaar als alles-min-één gesorteerd is, want als je bij de laatste twee bent aangekomen wordt de kleinste van die twee op de één na laatste plaats gezet, en dan staat de grootste dus automatisch achteraan.

Hier onder zie je een applet (gemaakt door Robert Lafore), waarin dat gedemonstreerd wordt.


New Genereert nieuwe data en initialiseert. De optie 'new' wisselt tussen random en aflopend.

Size Wisselt tussen 10 en 100 balkjes. Genereert ook nieuwe data en initialiseert.

Draw Tekent de balkjes opnieuw.

Run Start het automatisch sorteren (klik op 'Step' om te pauzeren en 'Run' om weer verder te gaan).

Step Voert één stap van het sorteerproces uit.

In het volgende voorbeeld demonstreren we het met de namen uit paragraaf 6.5.1.
Je begint als volgt:



Het resultaat van deze Verwissel(1,4)-bewerking zie je in de bovenste rij hier onder.
En ook hoe het verder gaat.
Het donker gearceerde geeft steeds de volgende gevonden kleinste waarde aan.
Het oranje gearceerde geeft steeds de het al gesorteerde deel aan.



We zetten nu eerst in een structuurdiagram wat er in grote lijnen moet gebeuren, dat is eigenlijk het zelfde als in de vorige paragraaf:



De verfijning van het eerste deelprobleem (namen invoeren) en van het laatste deelprobleem (gesorteerde lijst afdrukken) gaat net zo als in de vorige paragraaf.
Het tweede deelprobleem (sorteren) gaan we ook nu niet direkt tot in de finesses verfijnen, we doen het weer in etappes.
Eerst verfijnen we het als volgt:



Het deelprobleem Zoek de kleinste vanaf teller gaan we nu verder verfijnen.
In een structuurdiagram wordt dat het volgende:



Het deelprobleem: Verwissel de kleinste met het woord op plaats teller hebben we in de vorige paragraaf al besproken.

Nu gaan we het struktuurdiagram omzetten in een programma.
Van elke deelprobleem, dat we hebben verfijnd in een nieuw struktuurdiagram, maken we weer een procedure.
We voegen de header toe, en de declaraties. En dan krijgen we het volgende programma:

program selectionsort;

var aantal: integer;
   namen: array [1..100] of string;

procedure invoeren;
var naam: string;
   i: integer;
begin
   {de teller krijgt eerst de waarde 1}
   i:=1;
   {de variabele naam moet een (willekeurige) waarde krijgen
   want als naam gelijk is aan de lege string zal de  
   while-lus direkt stoppen}
   naam:="test";
   while naam<>"" do
   begin
      write("Geef naam nummer ",i:2,"        ");
      readln(naam);
      namen[i]:=naam;
      {voor het vervolg de letter met 1 ophogen}
      i:=i+1;
   end;
   {als er gestopt wordt is het aantal namen gelijk aan i-1}
   aantal:=i-1;
end;

procedure verwissel(var a:string; var b:string);
var hulp: string;
begin
   hulp:=a;
   a:=b;
   b:=hulp;
end;

procedure zoekkleinste(teller:integer; var nummerkleinste: integer);
var i: integer;
    kleinste: string;
begin
   kleinste:=namen[teller];
   nummerkleinste:=teller;
   for i:=teller to aantal do
   begin
      if (namen[i]<=kleinste ) then
      begin
         kleinste:=namen[i];
         nummerkleinste:=i;
      end;
   end;
end;

procedure sorteer;
var i, nummerkleinste: integer;
begin
for i:=1 to aantal-1 do
   begin
      zoekkleinste(i,nummerkleinste);
      verwissel(namen[i],namen[nummerkleinste]);
   end;
end;

procedure drukaf;
var i:integer;
begin
   writeln;writeln;writeln("de gesorteerde lijst:");
   for i:=1 to aantal do
   writeln(namen[i]);
end;


begin
   invoeren;
   sorteer;
   drukaf;
end.

en als we het programma dan uitvoeren kan het volgende verschijnen:



Opgaven.
Maak nu opgave 18 van hoofdstuk 6 (Pascal)